Merge "Change BiometricService/AuthController interface to sensorId"
diff --git a/Android.bp b/Android.bp
index f3225e2..e59b661 100644
--- a/Android.bp
+++ b/Android.bp
@@ -14,6 +14,37 @@
// Build the master framework library.
+// Defaults for platform code that runs inside system_server
+java_defaults {
+ name: "platform_service_defaults",
+ plugins: [
+ "error_prone_android_framework",
+ ],
+ errorprone: {
+ javacflags: [
+ "-Xep:AndroidFrameworkBinderIdentity:ERROR",
+ "-Xep:AndroidFrameworkCompatChange:ERROR",
+ "-Xep:AndroidFrameworkUid:ERROR",
+ ],
+ },
+}
+
+// Defaults for platform apps
+java_defaults {
+ name: "platform_app_defaults",
+ plugins: [
+ "error_prone_android_framework",
+ ],
+ errorprone: {
+ javacflags: [
+ // We're less worried about performance in app code
+ "-Xep:AndroidFrameworkEfficientCollections:OFF",
+ "-Xep:AndroidFrameworkEfficientParcelable:OFF",
+ "-Xep:AndroidFrameworkEfficientStrings:OFF",
+ ],
+ },
+}
+
// READ ME: ########################################################
//
// When updating this list of aidl files, consider if that aidl is
@@ -349,9 +380,6 @@
// etc.
":framework-javastream-protos",
":statslog-framework-java-gen", // FrameworkStatsLog.java
-
- // telephony annotations
- ":framework-telephony-annotations",
],
}
@@ -1239,23 +1267,6 @@
}
// Avoid including Parcelable classes as we don't want to have two copies of
-// Parcelable cross the process. This is used by framework-telephony (frameworks/base/telephony).
-filegroup {
- name: "framework-telephony-shared-srcs",
- srcs: [
- "core/java/android/util/IndentingPrintWriter.java",
- "core/java/android/util/RecurrenceRule.java",
- "core/java/com/android/internal/os/SomeArgs.java",
- "core/java/com/android/internal/util/BitwiseInputStream.java",
- "core/java/com/android/internal/util/BitwiseOutputStream.java",
- "core/java/com/android/internal/util/FunctionalUtils.java",
- "core/java/com/android/internal/util/HexDump.java",
- "core/java/com/android/internal/util/IndentingPrintWriter.java",
- "core/java/com/android/internal/util/Preconditions.java",
- ],
-}
-
-// Avoid including Parcelable classes as we don't want to have two copies of
// Parcelable cross the process.
filegroup {
name: "framework-cellbroadcast-shared-srcs",
@@ -1352,73 +1363,6 @@
"ApiDocs.bp",
]
-// TODO(b/147699819): move to frameworks/base/telephony/ folder
-droidstubs {
- name: "framework-telephony-stubs-srcs",
- srcs: [
- ":framework-telephony-sources",
- ":framework_native_aidl",
- ":framework-javastream-protos",
- ],
- aidl: {
- local_include_dirs: [
- "core/java",
- "telecomm/java"
- ],
- },
- libs: [
- "framework-annotations-lib",
- "android.hardware.radio-V1.6-java",
- ],
- check_api: {
- current: {
- // TODO(b/147699819): remove telephony prefix when moved
- api_file: "telephony/api/system-current.txt",
- removed_api_file: "telephony/api/system-removed.txt",
- },
- },
- // TODO: make telephony inherit the shared stubs and remove this
- args: "--show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
- "\\) " +
- "--error UnhiddenSystemApi " +
- "--hide BroadcastBehavior " +
- "--hide DeprecationMismatch " +
- "--hide HiddenSuperclass " +
- "--hide HiddenTypedefConstant " +
- "--hide HiddenTypeParameter " +
- "--hide MissingPermission " +
- "--hide RequiresPermission " +
- "--hide SdkConstant " +
- "--hide Todo " +
- "--hide Typo " +
- "--hide UnavailableSymbol ",
- filter_packages: ["android.telephony"],
- sdk_version: "system_current",
-}
-
-java_library {
- name: "framework-telephony-stubs",
- srcs: [":framework-telephony-stubs-srcs"],
- // TODO(b/147699819): move public aidls to a separate folder and potentially remove
- // below aidl exports.
- aidl: {
- export_include_dirs: ["telephony/java"],
- },
- sdk_version: "module_current",
-}
-
-filegroup {
- // TODO (b/147690217): move to frameworks/base/telephony/common.
- name: "framework-telephony-annotations",
- srcs: ["telephony/java/android/telephony/Annotation.java"],
-}
-
-filegroup {
- name: "framework-telephony-jarjar-rules",
- srcs: ["telephony/framework-telephony-jarjar-rules.txt"],
-}
-
// protolog start
filegroup {
name: "protolog-common-src",
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index f66d12a..cdf5df6c 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -9,6 +9,7 @@
cmds/uinput/
core/jni/
libs/input/
+ native/
services/core/jni/
services/incremental/
tests/
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 9604466..754c4e9 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -77,6 +77,7 @@
"android.hardware.vibrator-V1.3-java",
"framework-protos",
],
+ high_mem: true, // Lots of sources => high memory use, see b/170701554
installable: false,
annotations_enabled: true,
previous_api: ":android.api.public.latest",
diff --git a/apct-tests/perftests/core/src/android/content/pm/PackageManagerBenchmark.java b/apct-tests/perftests/core/src/android/content/pm/PackageManagerBenchmark.java
new file mode 100644
index 0000000..a82fab4
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/content/pm/PackageManagerBenchmark.java
@@ -0,0 +1,69 @@
+/*
+ * 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.Context;
+import android.os.UserHandle;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class PackageManagerBenchmark {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void createUserContextBenchmark() {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ context.createContextAsUser(UserHandle.SYSTEM, /* flags */ 0);
+ }
+ }
+
+ @Test
+ public void getResourcesForApplication_byStarAsUser()
+ throws PackageManager.NameNotFoundException {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ context.getPackageManager().getResourcesForApplicationAsUser(context.getPackageName(),
+ UserHandle.USER_SYSTEM);
+ }
+ }
+
+ @Test
+ public void getResourcesApplication_byCreateContextAsUser()
+ throws PackageManager.NameNotFoundException {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ while (state.keepRunning()) {
+ context.createContextAsUser(UserHandle.SYSTEM, /* flags */ 0).getPackageManager()
+ .getResourcesForApplication(context.getPackageName());
+ }
+ }
+}
diff --git a/apct-tests/perftests/textclassifier/run.sh b/apct-tests/perftests/textclassifier/run.sh
index d36d190..9a0f4f9 100755
--- a/apct-tests/perftests/textclassifier/run.sh
+++ b/apct-tests/perftests/textclassifier/run.sh
@@ -1,8 +1,8 @@
set -e
-build/soong/soong_ui.bash --make-mode TextClassifierPerfTests perf-setup.sh
+build/soong/soong_ui.bash --make-mode TextClassifierPerfTests perf-setup
adb install ${OUT}/testcases/TextClassifierPerfTests/arm64/TextClassifierPerfTests.apk
adb shell cmd package compile -m speed -f com.android.perftests.textclassifier
-adb push ${OUT}/obj/EXECUTABLES/perf-setup.sh_intermediates/perf-setup.sh /data/local/tmp/
+adb push ${OUT}/obj/EXECUTABLES/perf-setup_intermediates/perf-setup.sh /data/local/tmp/
adb shell chmod +x /data/local/tmp/perf-setup.sh
adb shell /data/local/tmp/perf-setup.sh
-adb shell am instrument -w -e package android.view.textclassifier com.android.perftests.textclassifier/androidx.test.runner.AndroidJUnitRunner
\ No newline at end of file
+adb shell am instrument -w -e package android.view.textclassifier com.android.perftests.textclassifier/androidx.test.runner.AndroidJUnitRunner
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 6c14233..34e82b0 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -339,6 +339,11 @@
public void onPropertiesChanged(DeviceConfig.Properties properties) {
boolean apiQuotaScheduleUpdated = false;
boolean concurrencyUpdated = false;
+ for (int controller = 0; controller < mControllers.size(); controller++) {
+ final StateController sc = mControllers.get(controller);
+ sc.prepareForUpdatedConstantsLocked();
+ }
+
synchronized (mLock) {
for (String name : properties.getKeyset()) {
if (name == null) {
@@ -384,6 +389,11 @@
&& !concurrencyUpdated) {
mConstants.updateConcurrencyConstantsLocked();
concurrencyUpdated = true;
+ } else {
+ for (int ctrlr = 0; ctrlr < mControllers.size(); ctrlr++) {
+ final StateController sc = mControllers.get(ctrlr);
+ sc.processConstantLocked(properties, name);
+ }
}
break;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index c7cc2f0..00dbb82 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -1388,6 +1388,11 @@
}
if (isReady()) {
sb.append(" READY");
+ } else {
+ sb.append(" satisfied:0x").append(Integer.toHexString(satisfiedConstraints));
+ sb.append(" unsatisfied:0x").append(Integer.toHexString(
+ (satisfiedConstraints & mRequiredConstraintsOfInterest)
+ ^ mRequiredConstraintsOfInterest));
}
sb.append("}");
return sb.toString();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index c06e19c..b7ace70 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -37,12 +37,9 @@
import android.app.AppGlobals;
import android.app.IUidObserver;
import android.content.BroadcastReceiver;
-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.BatteryManager;
import android.os.BatteryManagerInternal;
import android.os.Handler;
@@ -50,10 +47,9 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.Settings;
+import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -494,7 +490,7 @@
mChargeTracker.startTracking();
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
- mQcConstants = new QcConstants(mHandler);
+ mQcConstants = new QcConstants();
final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
mContext.registerReceiverAsUser(mPackageAddedReceiver, UserHandle.ALL, filter, null, null);
@@ -513,11 +509,6 @@
}
@Override
- public void onSystemServicesReady() {
- mQcConstants.start(mContext.getContentResolver());
- }
-
- @Override
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
final int userId = jobStatus.getSourceUserId();
final String pkgName = jobStatus.getSourcePackageName();
@@ -2028,38 +2019,109 @@
}
}
- @VisibleForTesting
- class QcConstants extends ContentObserver {
- private ContentResolver mResolver;
- private final KeyValueListParser mParser = new KeyValueListParser(',');
+ @Override
+ public void prepareForUpdatedConstantsLocked() {
+ mQcConstants.mShouldReevaluateConstraints = false;
+ mQcConstants.mRateLimitingConstantsUpdated = false;
+ mQcConstants.mExecutionPeriodConstantsUpdated = false;
+ }
- private static final String KEY_ALLOWED_TIME_PER_PERIOD_MS = "allowed_time_per_period_ms";
- private static final String KEY_IN_QUOTA_BUFFER_MS = "in_quota_buffer_ms";
- private static final String KEY_WINDOW_SIZE_ACTIVE_MS = "window_size_active_ms";
- private static final String KEY_WINDOW_SIZE_WORKING_MS = "window_size_working_ms";
- private static final String KEY_WINDOW_SIZE_FREQUENT_MS = "window_size_frequent_ms";
- private static final String KEY_WINDOW_SIZE_RARE_MS = "window_size_rare_ms";
- private static final String KEY_WINDOW_SIZE_RESTRICTED_MS = "window_size_restricted_ms";
- private static final String KEY_MAX_EXECUTION_TIME_MS = "max_execution_time_ms";
- private static final String KEY_MAX_JOB_COUNT_ACTIVE = "max_job_count_active";
- private static final String KEY_MAX_JOB_COUNT_WORKING = "max_job_count_working";
- private static final String KEY_MAX_JOB_COUNT_FREQUENT = "max_job_count_frequent";
- private static final String KEY_MAX_JOB_COUNT_RARE = "max_job_count_rare";
- private static final String KEY_MAX_JOB_COUNT_RESTRICTED = "max_job_count_restricted";
- private static final String KEY_RATE_LIMITING_WINDOW_MS = "rate_limiting_window_ms";
- private static final String KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW =
- "max_job_count_per_rate_limiting_window";
- private static final String KEY_MAX_SESSION_COUNT_ACTIVE = "max_session_count_active";
- private static final String KEY_MAX_SESSION_COUNT_WORKING = "max_session_count_working";
- private static final String KEY_MAX_SESSION_COUNT_FREQUENT = "max_session_count_frequent";
- private static final String KEY_MAX_SESSION_COUNT_RARE = "max_session_count_rare";
- private static final String KEY_MAX_SESSION_COUNT_RESTRICTED =
- "max_session_count_restricted";
- private static final String KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW =
- "max_session_count_per_rate_limiting_window";
- private static final String KEY_TIMING_SESSION_COALESCING_DURATION_MS =
- "timing_session_coalescing_duration_ms";
- private static final String KEY_MIN_QUOTA_CHECK_DELAY_MS = "min_quota_check_delay_ms";
+ @Override
+ public void processConstantLocked(DeviceConfig.Properties properties, String key) {
+ mQcConstants.processConstantLocked(properties, key);
+ }
+
+ @Override
+ public void onConstantsUpdatedLocked() {
+ if (mQcConstants.mShouldReevaluateConstraints) {
+ // Update job bookkeeping out of band.
+ JobSchedulerBackgroundThread.getHandler().post(() -> {
+ synchronized (mLock) {
+ invalidateAllExecutionStatsLocked();
+ maybeUpdateAllConstraintsLocked();
+ }
+ });
+ }
+ }
+
+ @VisibleForTesting
+ class QcConstants {
+ private boolean mShouldReevaluateConstraints = false;
+ private boolean mRateLimitingConstantsUpdated = false;
+ private boolean mExecutionPeriodConstantsUpdated = false;
+
+ /** Prefix to use with all constant keys in order to "sub-namespace" the keys. */
+ private static final String QC_CONSTANT_PREFIX = "qc_";
+
+ @VisibleForTesting
+ static final String KEY_ALLOWED_TIME_PER_PERIOD_MS =
+ QC_CONSTANT_PREFIX + "allowed_time_per_period_ms";
+ @VisibleForTesting
+ static final String KEY_IN_QUOTA_BUFFER_MS =
+ QC_CONSTANT_PREFIX + "in_quota_buffer_ms";
+ @VisibleForTesting
+ static final String KEY_WINDOW_SIZE_ACTIVE_MS =
+ QC_CONSTANT_PREFIX + "window_size_active_ms";
+ @VisibleForTesting
+ static final String KEY_WINDOW_SIZE_WORKING_MS =
+ QC_CONSTANT_PREFIX + "window_size_working_ms";
+ @VisibleForTesting
+ static final String KEY_WINDOW_SIZE_FREQUENT_MS =
+ QC_CONSTANT_PREFIX + "window_size_frequent_ms";
+ @VisibleForTesting
+ static final String KEY_WINDOW_SIZE_RARE_MS =
+ QC_CONSTANT_PREFIX + "window_size_rare_ms";
+ @VisibleForTesting
+ static final String KEY_WINDOW_SIZE_RESTRICTED_MS =
+ QC_CONSTANT_PREFIX + "window_size_restricted_ms";
+ @VisibleForTesting
+ static final String KEY_MAX_EXECUTION_TIME_MS =
+ QC_CONSTANT_PREFIX + "max_execution_time_ms";
+ @VisibleForTesting
+ static final String KEY_MAX_JOB_COUNT_ACTIVE =
+ QC_CONSTANT_PREFIX + "max_job_count_active";
+ @VisibleForTesting
+ static final String KEY_MAX_JOB_COUNT_WORKING =
+ QC_CONSTANT_PREFIX + "max_job_count_working";
+ @VisibleForTesting
+ static final String KEY_MAX_JOB_COUNT_FREQUENT =
+ QC_CONSTANT_PREFIX + "max_job_count_frequent";
+ @VisibleForTesting
+ static final String KEY_MAX_JOB_COUNT_RARE =
+ QC_CONSTANT_PREFIX + "max_job_count_rare";
+ @VisibleForTesting
+ static final String KEY_MAX_JOB_COUNT_RESTRICTED =
+ QC_CONSTANT_PREFIX + "max_job_count_restricted";
+ @VisibleForTesting
+ static final String KEY_RATE_LIMITING_WINDOW_MS =
+ QC_CONSTANT_PREFIX + "rate_limiting_window_ms";
+ @VisibleForTesting
+ static final String KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW =
+ QC_CONSTANT_PREFIX + "max_job_count_per_rate_limiting_window";
+ @VisibleForTesting
+ static final String KEY_MAX_SESSION_COUNT_ACTIVE =
+ QC_CONSTANT_PREFIX + "max_session_count_active";
+ @VisibleForTesting
+ static final String KEY_MAX_SESSION_COUNT_WORKING =
+ QC_CONSTANT_PREFIX + "max_session_count_working";
+ @VisibleForTesting
+ static final String KEY_MAX_SESSION_COUNT_FREQUENT =
+ QC_CONSTANT_PREFIX + "max_session_count_frequent";
+ @VisibleForTesting
+ static final String KEY_MAX_SESSION_COUNT_RARE =
+ QC_CONSTANT_PREFIX + "max_session_count_rare";
+ @VisibleForTesting
+ static final String KEY_MAX_SESSION_COUNT_RESTRICTED =
+ QC_CONSTANT_PREFIX + "max_session_count_restricted";
+ @VisibleForTesting
+ static final String KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW =
+ QC_CONSTANT_PREFIX + "max_session_count_per_rate_limiting_window";
+ @VisibleForTesting
+ static final String KEY_TIMING_SESSION_COALESCING_DURATION_MS =
+ QC_CONSTANT_PREFIX + "timing_session_coalescing_duration_ms";
+ @VisibleForTesting
+ static final String KEY_MIN_QUOTA_CHECK_DELAY_MS =
+ QC_CONSTANT_PREFIX + "min_quota_check_delay_ms";
private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_MS =
10 * 60 * 1000L; // 10 minutes
@@ -2260,238 +2322,273 @@
/** The minimum value that {@link #RATE_LIMITING_WINDOW_MS} can have. */
private static final long MIN_RATE_LIMITING_WINDOW_MS = 30 * SECOND_IN_MILLIS;
- QcConstants(Handler handler) {
- super(handler);
- }
+ public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
+ @NonNull String key) {
+ switch (key) {
+ case KEY_ALLOWED_TIME_PER_PERIOD_MS:
+ case KEY_IN_QUOTA_BUFFER_MS:
+ case KEY_MAX_EXECUTION_TIME_MS:
+ case KEY_WINDOW_SIZE_ACTIVE_MS:
+ case KEY_WINDOW_SIZE_WORKING_MS:
+ case KEY_WINDOW_SIZE_FREQUENT_MS:
+ case KEY_WINDOW_SIZE_RARE_MS:
+ case KEY_WINDOW_SIZE_RESTRICTED_MS:
+ updateExecutionPeriodConstantsLocked();
+ break;
- private void start(ContentResolver resolver) {
- mResolver = resolver;
- mResolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS), false, this);
- onChange(true, null);
- }
+ case KEY_RATE_LIMITING_WINDOW_MS:
+ case KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW:
+ case KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW:
+ updateRateLimitingConstantsLocked();
+ break;
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- final String constants = Settings.Global.getString(
- mResolver, Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS);
-
- try {
- mParser.setString(constants);
- } catch (Exception e) {
- // Failed to parse the settings string, log this and move on with defaults.
- Slog.e(TAG, "Bad jobscheduler quota controller settings", e);
+ case KEY_MAX_JOB_COUNT_ACTIVE:
+ MAX_JOB_COUNT_ACTIVE = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_ACTIVE);
+ int newActiveMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_ACTIVE);
+ if (mMaxBucketJobCounts[ACTIVE_INDEX] != newActiveMaxJobCount) {
+ mMaxBucketJobCounts[ACTIVE_INDEX] = newActiveMaxJobCount;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
+ case KEY_MAX_JOB_COUNT_WORKING:
+ MAX_JOB_COUNT_WORKING = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_WORKING);
+ int newWorkingMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT,
+ MAX_JOB_COUNT_WORKING);
+ if (mMaxBucketJobCounts[WORKING_INDEX] != newWorkingMaxJobCount) {
+ mMaxBucketJobCounts[WORKING_INDEX] = newWorkingMaxJobCount;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
+ case KEY_MAX_JOB_COUNT_FREQUENT:
+ MAX_JOB_COUNT_FREQUENT = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_FREQUENT);
+ int newFrequentMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT,
+ MAX_JOB_COUNT_FREQUENT);
+ if (mMaxBucketJobCounts[FREQUENT_INDEX] != newFrequentMaxJobCount) {
+ mMaxBucketJobCounts[FREQUENT_INDEX] = newFrequentMaxJobCount;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
+ case KEY_MAX_JOB_COUNT_RARE:
+ MAX_JOB_COUNT_RARE = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_RARE);
+ int newRareMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_RARE);
+ if (mMaxBucketJobCounts[RARE_INDEX] != newRareMaxJobCount) {
+ mMaxBucketJobCounts[RARE_INDEX] = newRareMaxJobCount;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
+ case KEY_MAX_JOB_COUNT_RESTRICTED:
+ MAX_JOB_COUNT_RESTRICTED =
+ properties.getInt(key, DEFAULT_MAX_JOB_COUNT_RESTRICTED);
+ int newRestrictedMaxJobCount =
+ Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_RESTRICTED);
+ if (mMaxBucketJobCounts[RESTRICTED_INDEX] != newRestrictedMaxJobCount) {
+ mMaxBucketJobCounts[RESTRICTED_INDEX] = newRestrictedMaxJobCount;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
+ case KEY_MAX_SESSION_COUNT_ACTIVE:
+ MAX_SESSION_COUNT_ACTIVE =
+ properties.getInt(key, DEFAULT_MAX_SESSION_COUNT_ACTIVE);
+ int newActiveMaxSessionCount =
+ Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_ACTIVE);
+ if (mMaxBucketSessionCounts[ACTIVE_INDEX] != newActiveMaxSessionCount) {
+ mMaxBucketSessionCounts[ACTIVE_INDEX] = newActiveMaxSessionCount;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
+ case KEY_MAX_SESSION_COUNT_WORKING:
+ MAX_SESSION_COUNT_WORKING =
+ properties.getInt(key, DEFAULT_MAX_SESSION_COUNT_WORKING);
+ int newWorkingMaxSessionCount =
+ Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_WORKING);
+ if (mMaxBucketSessionCounts[WORKING_INDEX] != newWorkingMaxSessionCount) {
+ mMaxBucketSessionCounts[WORKING_INDEX] = newWorkingMaxSessionCount;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
+ case KEY_MAX_SESSION_COUNT_FREQUENT:
+ MAX_SESSION_COUNT_FREQUENT =
+ properties.getInt(key, DEFAULT_MAX_SESSION_COUNT_FREQUENT);
+ int newFrequentMaxSessionCount =
+ Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_FREQUENT);
+ if (mMaxBucketSessionCounts[FREQUENT_INDEX] != newFrequentMaxSessionCount) {
+ mMaxBucketSessionCounts[FREQUENT_INDEX] = newFrequentMaxSessionCount;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
+ case KEY_MAX_SESSION_COUNT_RARE:
+ MAX_SESSION_COUNT_RARE = properties.getInt(key, DEFAULT_MAX_SESSION_COUNT_RARE);
+ int newRareMaxSessionCount =
+ Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_RARE);
+ if (mMaxBucketSessionCounts[RARE_INDEX] != newRareMaxSessionCount) {
+ mMaxBucketSessionCounts[RARE_INDEX] = newRareMaxSessionCount;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
+ case KEY_MAX_SESSION_COUNT_RESTRICTED:
+ MAX_SESSION_COUNT_RESTRICTED =
+ properties.getInt(key, DEFAULT_MAX_SESSION_COUNT_RESTRICTED);
+ int newRestrictedMaxSessionCount = Math.max(0, MAX_SESSION_COUNT_RESTRICTED);
+ if (mMaxBucketSessionCounts[RESTRICTED_INDEX] != newRestrictedMaxSessionCount) {
+ mMaxBucketSessionCounts[RESTRICTED_INDEX] = newRestrictedMaxSessionCount;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
+ case KEY_TIMING_SESSION_COALESCING_DURATION_MS:
+ TIMING_SESSION_COALESCING_DURATION_MS =
+ properties.getLong(key, DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS);
+ long newSessionCoalescingDurationMs = Math.min(15 * MINUTE_IN_MILLIS,
+ Math.max(0, TIMING_SESSION_COALESCING_DURATION_MS));
+ if (mTimingSessionCoalescingDurationMs != newSessionCoalescingDurationMs) {
+ mTimingSessionCoalescingDurationMs = newSessionCoalescingDurationMs;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
+ case KEY_MIN_QUOTA_CHECK_DELAY_MS:
+ MIN_QUOTA_CHECK_DELAY_MS =
+ properties.getLong(key, DEFAULT_MIN_QUOTA_CHECK_DELAY_MS);
+ // We don't need to re-evaluate execution stats or constraint status for this.
+ // Limit the delay to the range [0, 15] minutes.
+ mInQuotaAlarmListener.setMinQuotaCheckDelayMs(
+ Math.min(15 * MINUTE_IN_MILLIS, Math.max(0, MIN_QUOTA_CHECK_DELAY_MS)));
+ break;
}
-
- ALLOWED_TIME_PER_PERIOD_MS = mParser.getDurationMillis(
- KEY_ALLOWED_TIME_PER_PERIOD_MS, DEFAULT_ALLOWED_TIME_PER_PERIOD_MS);
- IN_QUOTA_BUFFER_MS = mParser.getDurationMillis(
- KEY_IN_QUOTA_BUFFER_MS, DEFAULT_IN_QUOTA_BUFFER_MS);
- WINDOW_SIZE_ACTIVE_MS = mParser.getDurationMillis(
- KEY_WINDOW_SIZE_ACTIVE_MS, DEFAULT_WINDOW_SIZE_ACTIVE_MS);
- WINDOW_SIZE_WORKING_MS = mParser.getDurationMillis(
- KEY_WINDOW_SIZE_WORKING_MS, DEFAULT_WINDOW_SIZE_WORKING_MS);
- WINDOW_SIZE_FREQUENT_MS = mParser.getDurationMillis(
- KEY_WINDOW_SIZE_FREQUENT_MS, DEFAULT_WINDOW_SIZE_FREQUENT_MS);
- WINDOW_SIZE_RARE_MS = mParser.getDurationMillis(
- KEY_WINDOW_SIZE_RARE_MS, DEFAULT_WINDOW_SIZE_RARE_MS);
- WINDOW_SIZE_RESTRICTED_MS = mParser.getDurationMillis(
- KEY_WINDOW_SIZE_RESTRICTED_MS, DEFAULT_WINDOW_SIZE_RESTRICTED_MS);
- MAX_EXECUTION_TIME_MS = mParser.getDurationMillis(
- KEY_MAX_EXECUTION_TIME_MS, DEFAULT_MAX_EXECUTION_TIME_MS);
- MAX_JOB_COUNT_ACTIVE = mParser.getInt(
- KEY_MAX_JOB_COUNT_ACTIVE, DEFAULT_MAX_JOB_COUNT_ACTIVE);
- MAX_JOB_COUNT_WORKING = mParser.getInt(
- KEY_MAX_JOB_COUNT_WORKING, DEFAULT_MAX_JOB_COUNT_WORKING);
- MAX_JOB_COUNT_FREQUENT = mParser.getInt(
- KEY_MAX_JOB_COUNT_FREQUENT, DEFAULT_MAX_JOB_COUNT_FREQUENT);
- MAX_JOB_COUNT_RARE = mParser.getInt(
- KEY_MAX_JOB_COUNT_RARE, DEFAULT_MAX_JOB_COUNT_RARE);
- MAX_JOB_COUNT_RESTRICTED = mParser.getInt(
- KEY_MAX_JOB_COUNT_RESTRICTED, DEFAULT_MAX_JOB_COUNT_RESTRICTED);
- RATE_LIMITING_WINDOW_MS = mParser.getLong(
- KEY_RATE_LIMITING_WINDOW_MS, DEFAULT_RATE_LIMITING_WINDOW_MS);
- MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = mParser.getInt(
- KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
- DEFAULT_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW);
- MAX_SESSION_COUNT_ACTIVE = mParser.getInt(
- KEY_MAX_SESSION_COUNT_ACTIVE, DEFAULT_MAX_SESSION_COUNT_ACTIVE);
- MAX_SESSION_COUNT_WORKING = mParser.getInt(
- KEY_MAX_SESSION_COUNT_WORKING, DEFAULT_MAX_SESSION_COUNT_WORKING);
- MAX_SESSION_COUNT_FREQUENT = mParser.getInt(
- KEY_MAX_SESSION_COUNT_FREQUENT, DEFAULT_MAX_SESSION_COUNT_FREQUENT);
- MAX_SESSION_COUNT_RARE = mParser.getInt(
- KEY_MAX_SESSION_COUNT_RARE, DEFAULT_MAX_SESSION_COUNT_RARE);
- MAX_SESSION_COUNT_RESTRICTED = mParser.getInt(
- KEY_MAX_SESSION_COUNT_RESTRICTED, DEFAULT_MAX_SESSION_COUNT_RESTRICTED);
- MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = mParser.getInt(
- KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
- DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
- TIMING_SESSION_COALESCING_DURATION_MS = mParser.getLong(
- KEY_TIMING_SESSION_COALESCING_DURATION_MS,
- DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS);
- MIN_QUOTA_CHECK_DELAY_MS = mParser.getDurationMillis(KEY_MIN_QUOTA_CHECK_DELAY_MS,
- DEFAULT_MIN_QUOTA_CHECK_DELAY_MS);
-
- updateConstants();
}
- @VisibleForTesting
- void updateConstants() {
- synchronized (mLock) {
- boolean changed = false;
+ private void updateExecutionPeriodConstantsLocked() {
+ if (mExecutionPeriodConstantsUpdated) {
+ return;
+ }
+ mExecutionPeriodConstantsUpdated = true;
- long newMaxExecutionTimeMs = Math.max(MIN_MAX_EXECUTION_TIME_MS,
- Math.min(MAX_PERIOD_MS, MAX_EXECUTION_TIME_MS));
- if (mMaxExecutionTimeMs != newMaxExecutionTimeMs) {
- mMaxExecutionTimeMs = newMaxExecutionTimeMs;
- mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
- changed = true;
- }
- long newAllowedTimeMs = Math.min(mMaxExecutionTimeMs,
- Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_MS));
- if (mAllowedTimePerPeriodMs != newAllowedTimeMs) {
- mAllowedTimePerPeriodMs = newAllowedTimeMs;
- mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
- changed = true;
- }
- // Make sure quota buffer is non-negative, not greater than allowed time per period,
- // and no more than 5 minutes.
- long newQuotaBufferMs = Math.max(0, Math.min(mAllowedTimePerPeriodMs,
- Math.min(5 * MINUTE_IN_MILLIS, IN_QUOTA_BUFFER_MS)));
- if (mQuotaBufferMs != newQuotaBufferMs) {
- mQuotaBufferMs = newQuotaBufferMs;
- mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
- mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
- changed = true;
- }
- long newActivePeriodMs = Math.max(mAllowedTimePerPeriodMs,
- Math.min(MAX_PERIOD_MS, WINDOW_SIZE_ACTIVE_MS));
- if (mBucketPeriodsMs[ACTIVE_INDEX] != newActivePeriodMs) {
- mBucketPeriodsMs[ACTIVE_INDEX] = newActivePeriodMs;
- changed = true;
- }
- long newWorkingPeriodMs = Math.max(mAllowedTimePerPeriodMs,
- Math.min(MAX_PERIOD_MS, WINDOW_SIZE_WORKING_MS));
- if (mBucketPeriodsMs[WORKING_INDEX] != newWorkingPeriodMs) {
- mBucketPeriodsMs[WORKING_INDEX] = newWorkingPeriodMs;
- changed = true;
- }
- long newFrequentPeriodMs = Math.max(mAllowedTimePerPeriodMs,
- Math.min(MAX_PERIOD_MS, WINDOW_SIZE_FREQUENT_MS));
- if (mBucketPeriodsMs[FREQUENT_INDEX] != newFrequentPeriodMs) {
- mBucketPeriodsMs[FREQUENT_INDEX] = newFrequentPeriodMs;
- changed = true;
- }
- long newRarePeriodMs = Math.max(mAllowedTimePerPeriodMs,
- Math.min(MAX_PERIOD_MS, WINDOW_SIZE_RARE_MS));
- if (mBucketPeriodsMs[RARE_INDEX] != newRarePeriodMs) {
- mBucketPeriodsMs[RARE_INDEX] = newRarePeriodMs;
- changed = true;
- }
- // Fit in the range [allowed time (10 mins), 1 week].
- long newRestrictedPeriodMs = Math.max(mAllowedTimePerPeriodMs,
- Math.min(7 * 24 * 60 * MINUTE_IN_MILLIS, WINDOW_SIZE_RESTRICTED_MS));
- if (mBucketPeriodsMs[RESTRICTED_INDEX] != newRestrictedPeriodMs) {
- mBucketPeriodsMs[RESTRICTED_INDEX] = newRestrictedPeriodMs;
- changed = true;
- }
- long newRateLimitingWindowMs = Math.min(MAX_PERIOD_MS,
- Math.max(MIN_RATE_LIMITING_WINDOW_MS, RATE_LIMITING_WINDOW_MS));
- if (mRateLimitingWindowMs != newRateLimitingWindowMs) {
- mRateLimitingWindowMs = newRateLimitingWindowMs;
- changed = true;
- }
- int newMaxJobCountPerRateLimitingWindow = Math.max(
- MIN_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
- MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW);
- if (mMaxJobCountPerRateLimitingWindow != newMaxJobCountPerRateLimitingWindow) {
- mMaxJobCountPerRateLimitingWindow = newMaxJobCountPerRateLimitingWindow;
- changed = true;
- }
- int newActiveMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_ACTIVE);
- if (mMaxBucketJobCounts[ACTIVE_INDEX] != newActiveMaxJobCount) {
- mMaxBucketJobCounts[ACTIVE_INDEX] = newActiveMaxJobCount;
- changed = true;
- }
- int newWorkingMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_WORKING);
- if (mMaxBucketJobCounts[WORKING_INDEX] != newWorkingMaxJobCount) {
- mMaxBucketJobCounts[WORKING_INDEX] = newWorkingMaxJobCount;
- changed = true;
- }
- int newFrequentMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_FREQUENT);
- if (mMaxBucketJobCounts[FREQUENT_INDEX] != newFrequentMaxJobCount) {
- mMaxBucketJobCounts[FREQUENT_INDEX] = newFrequentMaxJobCount;
- changed = true;
- }
- int newRareMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_RARE);
- if (mMaxBucketJobCounts[RARE_INDEX] != newRareMaxJobCount) {
- mMaxBucketJobCounts[RARE_INDEX] = newRareMaxJobCount;
- changed = true;
- }
- int newRestrictedMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT,
- MAX_JOB_COUNT_RESTRICTED);
- if (mMaxBucketJobCounts[RESTRICTED_INDEX] != newRestrictedMaxJobCount) {
- mMaxBucketJobCounts[RESTRICTED_INDEX] = newRestrictedMaxJobCount;
- changed = true;
- }
- int newMaxSessionCountPerRateLimitPeriod = Math.max(
- MIN_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
- MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
- if (mMaxSessionCountPerRateLimitingWindow != newMaxSessionCountPerRateLimitPeriod) {
- mMaxSessionCountPerRateLimitingWindow = newMaxSessionCountPerRateLimitPeriod;
- changed = true;
- }
- int newActiveMaxSessionCount =
- Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_ACTIVE);
- if (mMaxBucketSessionCounts[ACTIVE_INDEX] != newActiveMaxSessionCount) {
- mMaxBucketSessionCounts[ACTIVE_INDEX] = newActiveMaxSessionCount;
- changed = true;
- }
- int newWorkingMaxSessionCount =
- Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_WORKING);
- if (mMaxBucketSessionCounts[WORKING_INDEX] != newWorkingMaxSessionCount) {
- mMaxBucketSessionCounts[WORKING_INDEX] = newWorkingMaxSessionCount;
- changed = true;
- }
- int newFrequentMaxSessionCount =
- Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_FREQUENT);
- if (mMaxBucketSessionCounts[FREQUENT_INDEX] != newFrequentMaxSessionCount) {
- mMaxBucketSessionCounts[FREQUENT_INDEX] = newFrequentMaxSessionCount;
- changed = true;
- }
- int newRareMaxSessionCount =
- Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_RARE);
- if (mMaxBucketSessionCounts[RARE_INDEX] != newRareMaxSessionCount) {
- mMaxBucketSessionCounts[RARE_INDEX] = newRareMaxSessionCount;
- changed = true;
- }
- int newRestrictedMaxSessionCount = Math.max(0, MAX_SESSION_COUNT_RESTRICTED);
- if (mMaxBucketSessionCounts[RESTRICTED_INDEX] != newRestrictedMaxSessionCount) {
- mMaxBucketSessionCounts[RESTRICTED_INDEX] = newRestrictedMaxSessionCount;
- changed = true;
- }
- long newSessionCoalescingDurationMs = Math.min(15 * MINUTE_IN_MILLIS,
- Math.max(0, TIMING_SESSION_COALESCING_DURATION_MS));
- if (mTimingSessionCoalescingDurationMs != newSessionCoalescingDurationMs) {
- mTimingSessionCoalescingDurationMs = newSessionCoalescingDurationMs;
- changed = true;
- }
- // Don't set changed to true for this one since we don't need to re-evaluate
- // execution stats or constraint status. Limit the delay to the range [0, 15]
- // minutes.
- mInQuotaAlarmListener.setMinQuotaCheckDelayMs(
- Math.min(15 * MINUTE_IN_MILLIS, Math.max(0, MIN_QUOTA_CHECK_DELAY_MS)));
+ // Query the values as an atomic set.
+ final DeviceConfig.Properties properties = DeviceConfig.getProperties(
+ DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ KEY_ALLOWED_TIME_PER_PERIOD_MS, KEY_IN_QUOTA_BUFFER_MS,
+ KEY_MAX_EXECUTION_TIME_MS, KEY_WINDOW_SIZE_ACTIVE_MS,
+ KEY_WINDOW_SIZE_WORKING_MS,
+ KEY_WINDOW_SIZE_FREQUENT_MS, KEY_WINDOW_SIZE_RARE_MS,
+ KEY_WINDOW_SIZE_RESTRICTED_MS);
+ ALLOWED_TIME_PER_PERIOD_MS =
+ properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_MS,
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_MS);
+ IN_QUOTA_BUFFER_MS = properties.getLong(KEY_IN_QUOTA_BUFFER_MS,
+ DEFAULT_IN_QUOTA_BUFFER_MS);
+ MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS,
+ DEFAULT_MAX_EXECUTION_TIME_MS);
+ WINDOW_SIZE_ACTIVE_MS = properties.getLong(KEY_WINDOW_SIZE_ACTIVE_MS,
+ DEFAULT_WINDOW_SIZE_ACTIVE_MS);
+ WINDOW_SIZE_WORKING_MS =
+ properties.getLong(KEY_WINDOW_SIZE_WORKING_MS, DEFAULT_WINDOW_SIZE_WORKING_MS);
+ WINDOW_SIZE_FREQUENT_MS =
+ properties.getLong(KEY_WINDOW_SIZE_FREQUENT_MS,
+ DEFAULT_WINDOW_SIZE_FREQUENT_MS);
+ WINDOW_SIZE_RARE_MS = properties.getLong(KEY_WINDOW_SIZE_RARE_MS,
+ DEFAULT_WINDOW_SIZE_RARE_MS);
+ WINDOW_SIZE_RESTRICTED_MS =
+ properties.getLong(KEY_WINDOW_SIZE_RESTRICTED_MS,
+ DEFAULT_WINDOW_SIZE_RESTRICTED_MS);
- if (changed) {
- // Update job bookkeeping out of band.
- JobSchedulerBackgroundThread.getHandler().post(() -> {
- synchronized (mLock) {
- invalidateAllExecutionStatsLocked();
- maybeUpdateAllConstraintsLocked();
- }
- });
- }
+ long newMaxExecutionTimeMs = Math.max(MIN_MAX_EXECUTION_TIME_MS,
+ Math.min(MAX_PERIOD_MS, MAX_EXECUTION_TIME_MS));
+ if (mMaxExecutionTimeMs != newMaxExecutionTimeMs) {
+ mMaxExecutionTimeMs = newMaxExecutionTimeMs;
+ mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
+ mShouldReevaluateConstraints = true;
+ }
+ long newAllowedTimeMs = Math.min(mMaxExecutionTimeMs,
+ Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_MS));
+ if (mAllowedTimePerPeriodMs != newAllowedTimeMs) {
+ mAllowedTimePerPeriodMs = newAllowedTimeMs;
+ mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
+ mShouldReevaluateConstraints = true;
+ }
+ // Make sure quota buffer is non-negative, not greater than allowed time per period,
+ // and no more than 5 minutes.
+ long newQuotaBufferMs = Math.max(0, Math.min(mAllowedTimePerPeriodMs,
+ Math.min(5 * MINUTE_IN_MILLIS, IN_QUOTA_BUFFER_MS)));
+ if (mQuotaBufferMs != newQuotaBufferMs) {
+ mQuotaBufferMs = newQuotaBufferMs;
+ mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
+ mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
+ mShouldReevaluateConstraints = true;
+ }
+ long newActivePeriodMs = Math.max(mAllowedTimePerPeriodMs,
+ Math.min(MAX_PERIOD_MS, WINDOW_SIZE_ACTIVE_MS));
+ if (mBucketPeriodsMs[ACTIVE_INDEX] != newActivePeriodMs) {
+ mBucketPeriodsMs[ACTIVE_INDEX] = newActivePeriodMs;
+ mShouldReevaluateConstraints = true;
+ }
+ long newWorkingPeriodMs = Math.max(mAllowedTimePerPeriodMs,
+ Math.min(MAX_PERIOD_MS, WINDOW_SIZE_WORKING_MS));
+ if (mBucketPeriodsMs[WORKING_INDEX] != newWorkingPeriodMs) {
+ mBucketPeriodsMs[WORKING_INDEX] = newWorkingPeriodMs;
+ mShouldReevaluateConstraints = true;
+ }
+ long newFrequentPeriodMs = Math.max(mAllowedTimePerPeriodMs,
+ Math.min(MAX_PERIOD_MS, WINDOW_SIZE_FREQUENT_MS));
+ if (mBucketPeriodsMs[FREQUENT_INDEX] != newFrequentPeriodMs) {
+ mBucketPeriodsMs[FREQUENT_INDEX] = newFrequentPeriodMs;
+ mShouldReevaluateConstraints = true;
+ }
+ long newRarePeriodMs = Math.max(mAllowedTimePerPeriodMs,
+ Math.min(MAX_PERIOD_MS, WINDOW_SIZE_RARE_MS));
+ if (mBucketPeriodsMs[RARE_INDEX] != newRarePeriodMs) {
+ mBucketPeriodsMs[RARE_INDEX] = newRarePeriodMs;
+ mShouldReevaluateConstraints = true;
+ }
+ // Fit in the range [allowed time (10 mins), 1 week].
+ long newRestrictedPeriodMs = Math.max(mAllowedTimePerPeriodMs,
+ Math.min(7 * 24 * 60 * MINUTE_IN_MILLIS, WINDOW_SIZE_RESTRICTED_MS));
+ if (mBucketPeriodsMs[RESTRICTED_INDEX] != newRestrictedPeriodMs) {
+ mBucketPeriodsMs[RESTRICTED_INDEX] = newRestrictedPeriodMs;
+ mShouldReevaluateConstraints = true;
+ }
+ }
+
+ private void updateRateLimitingConstantsLocked() {
+ if (mRateLimitingConstantsUpdated) {
+ return;
+ }
+ mRateLimitingConstantsUpdated = true;
+
+ // Query the values as an atomic set.
+ final DeviceConfig.Properties properties = DeviceConfig.getProperties(
+ DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ KEY_RATE_LIMITING_WINDOW_MS, KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
+ KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
+
+ RATE_LIMITING_WINDOW_MS =
+ properties.getLong(KEY_RATE_LIMITING_WINDOW_MS,
+ DEFAULT_RATE_LIMITING_WINDOW_MS);
+
+ MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW =
+ properties.getInt(KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
+ DEFAULT_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW);
+
+ MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW =
+ properties.getInt(KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
+ DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
+
+ long newRateLimitingWindowMs = Math.min(MAX_PERIOD_MS,
+ Math.max(MIN_RATE_LIMITING_WINDOW_MS, RATE_LIMITING_WINDOW_MS));
+ if (mRateLimitingWindowMs != newRateLimitingWindowMs) {
+ mRateLimitingWindowMs = newRateLimitingWindowMs;
+ mShouldReevaluateConstraints = true;
+ }
+ int newMaxJobCountPerRateLimitingWindow = Math.max(
+ MIN_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
+ MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW);
+ if (mMaxJobCountPerRateLimitingWindow != newMaxJobCountPerRateLimitingWindow) {
+ mMaxJobCountPerRateLimitingWindow = newMaxJobCountPerRateLimitingWindow;
+ mShouldReevaluateConstraints = true;
+ }
+ int newMaxSessionCountPerRateLimitPeriod = Math.max(
+ MIN_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
+ MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
+ if (mMaxSessionCountPerRateLimitingWindow != newMaxSessionCountPerRateLimitPeriod) {
+ mMaxSessionCountPerRateLimitingWindow = newMaxSessionCountPerRateLimitPeriod;
+ mShouldReevaluateConstraints = true;
}
}
@@ -2634,6 +2731,11 @@
}
@VisibleForTesting
+ long getMinQuotaCheckDelayMs() {
+ return mInQuotaAlarmListener.mMinQuotaCheckDelayMs;
+ }
+
+ @VisibleForTesting
long getRateLimitingWindowMs() {
return mRateLimitingWindowMs;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
index 71c7599..56b3090 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
@@ -18,7 +18,9 @@
import static com.android.server.job.JobSchedulerService.DEBUG;
+import android.annotation.NonNull;
import android.content.Context;
+import android.provider.DeviceConfig;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -84,6 +86,13 @@
public void rescheduleForFailureLocked(JobStatus newJob, JobStatus failureToReschedule) {
}
+ /** Notice that updated configuration constants are about to be read. */
+ public void prepareForUpdatedConstantsLocked() {}
+
+ /** Process the specified constant and update internal constants if relevant. */
+ public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
+ @NonNull String key) {}
+
/**
* Called when the JobScheduler.Constants are updated.
*/
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 813631e..b3c9a9a 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -35,7 +35,6 @@
libs: [
"framework_media_annotation",
],
-
static_libs: [
"exoplayer2-extractor"
],
@@ -111,10 +110,33 @@
impl_library_visibility: ["//frameworks/av/apex:__subpackages__"],
}
-
java_library {
name: "framework_media_annotation",
srcs: [":framework-media-annotation-srcs"],
installable: false,
sdk_version: "core_current",
}
+
+cc_library_shared {
+ name: "libmediaparser-jni",
+ srcs: [
+ "jni/android_media_MediaParserJNI.cpp",
+ ],
+ header_libs: ["jni_headers"],
+ shared_libs: [
+ "libandroid",
+ "liblog",
+ "libmediametrics",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wunreachable-code",
+ "-Wunused",
+ ],
+ apex_available: [
+ "com.android.media",
+ ],
+ min_sdk_version: "29",
+}
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index e4b5d19..045b413 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -75,6 +75,8 @@
import java.util.List;
import java.util.Map;
import java.util.UUID;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.function.Function;
/**
* Parses media container formats and extracts contained media samples and metadata.
@@ -882,6 +884,7 @@
// Private constants.
private static final String TAG = "MediaParser";
+ private static final String JNI_LIBRARY_NAME = "mediaparser-jni";
private static final Map<String, ExtractorFactory> EXTRACTOR_FACTORIES_BY_NAME;
private static final Map<String, Class> EXPECTED_TYPE_BY_PARAMETER_NAME;
private static final String TS_MODE_SINGLE_PMT = "single_pmt";
@@ -889,6 +892,14 @@
private static final String TS_MODE_HLS = "hls";
private static final int BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY = 6;
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+ private static final String MEDIAMETRICS_ELEMENT_SEPARATOR = "|";
+ private static final int MEDIAMETRICS_MAX_STRING_SIZE = 200;
+ private static final int MEDIAMETRICS_PARAMETER_LIST_MAX_LENGTH;
+ /**
+ * Intentional error introduced to reported metrics to prevent identification of the parsed
+ * media. Note: Increasing this value may cause older hostside CTS tests to fail.
+ */
+ private static final float MEDIAMETRICS_DITHER = .02f;
@IntDef(
value = {
@@ -920,7 +931,7 @@
@NonNull @ParserName String name, @NonNull OutputConsumer outputConsumer) {
String[] nameAsArray = new String[] {name};
assertValidNames(nameAsArray);
- return new MediaParser(outputConsumer, /* sniff= */ false, name);
+ return new MediaParser(outputConsumer, /* createdByName= */ true, name);
}
/**
@@ -940,7 +951,7 @@
if (parserNames.length == 0) {
parserNames = EXTRACTOR_FACTORIES_BY_NAME.keySet().toArray(new String[0]);
}
- return new MediaParser(outputConsumer, /* sniff= */ true, parserNames);
+ return new MediaParser(outputConsumer, /* createdByName= */ false, parserNames);
}
// Misc static methods.
@@ -1052,6 +1063,14 @@
private long mPendingSeekPosition;
private long mPendingSeekTimeMicros;
private boolean mLoggedSchemeInitDataCreationException;
+ private boolean mReleased;
+
+ // MediaMetrics fields.
+ private final boolean mCreatedByName;
+ private final SparseArray<Format> mTrackFormats;
+ private String mLastObservedExceptionName;
+ private long mDurationMillis;
+ private long mResourceByteCount;
// Public methods.
@@ -1166,11 +1185,16 @@
if (mExtractorInput == null) {
// TODO: For efficiency, the same implementation should be used, by providing a
// clearBuffers() method, or similar.
+ long resourceLength = seekableInputReader.getLength();
+ if (resourceLength == -1) {
+ mResourceByteCount = -1;
+ }
+ if (mResourceByteCount != -1) {
+ mResourceByteCount += resourceLength;
+ }
mExtractorInput =
new DefaultExtractorInput(
- mExoDataReader,
- seekableInputReader.getPosition(),
- seekableInputReader.getLength());
+ mExoDataReader, seekableInputReader.getPosition(), resourceLength);
}
mExoDataReader.mInputReader = seekableInputReader;
@@ -1195,7 +1219,10 @@
}
}
if (mExtractor == null) {
- throw UnrecognizedInputFormatException.createForExtractors(mParserNamesPool);
+ UnrecognizedInputFormatException exception =
+ UnrecognizedInputFormatException.createForExtractors(mParserNamesPool);
+ mLastObservedExceptionName = exception.getClass().getName();
+ throw exception;
}
return true;
}
@@ -1223,8 +1250,13 @@
int result;
try {
result = mExtractor.read(mExtractorInput, mPositionHolder);
- } catch (ParserException e) {
- throw new ParsingException(e);
+ } catch (Exception e) {
+ mLastObservedExceptionName = e.getClass().getName();
+ if (e instanceof ParserException) {
+ throw new ParsingException((ParserException) e);
+ } else {
+ throw e;
+ }
}
if (result == Extractor.RESULT_END_OF_INPUT) {
mExtractorInput = null;
@@ -1264,21 +1296,64 @@
* invoked.
*/
public void release() {
- // TODO: Dump media metrics here.
mExtractorInput = null;
mExtractor = null;
+ if (mReleased) {
+ // Nothing to do.
+ return;
+ }
+ mReleased = true;
+
+ String trackMimeTypes = buildMediaMetricsString(format -> format.sampleMimeType);
+ String trackCodecs = buildMediaMetricsString(format -> format.codecs);
+ int videoWidth = -1;
+ int videoHeight = -1;
+ for (int i = 0; i < mTrackFormats.size(); i++) {
+ Format format = mTrackFormats.valueAt(i);
+ if (format.width != Format.NO_VALUE && format.height != Format.NO_VALUE) {
+ videoWidth = format.width;
+ videoHeight = format.height;
+ break;
+ }
+ }
+
+ String alteredParameters =
+ String.join(
+ MEDIAMETRICS_ELEMENT_SEPARATOR,
+ mParserParameters.keySet().toArray(new String[0]));
+ alteredParameters =
+ alteredParameters.substring(
+ 0,
+ Math.min(
+ alteredParameters.length(),
+ MEDIAMETRICS_PARAMETER_LIST_MAX_LENGTH));
+
+ nativeSubmitMetrics(
+ mParserName,
+ mCreatedByName,
+ String.join(MEDIAMETRICS_ELEMENT_SEPARATOR, mParserNamesPool),
+ mLastObservedExceptionName,
+ addDither(mResourceByteCount),
+ addDither(mDurationMillis),
+ trackMimeTypes,
+ trackCodecs,
+ alteredParameters,
+ videoWidth,
+ videoHeight);
}
// Private methods.
- private MediaParser(OutputConsumer outputConsumer, boolean sniff, String... parserNamesPool) {
+ private MediaParser(
+ OutputConsumer outputConsumer, boolean createdByName, String... parserNamesPool) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
throw new UnsupportedOperationException("Android version must be R or greater.");
}
mParserParameters = new HashMap<>();
mOutputConsumer = outputConsumer;
mParserNamesPool = parserNamesPool;
- mParserName = sniff ? PARSER_NAME_UNKNOWN : parserNamesPool[0];
+ mCreatedByName = createdByName;
+ mParserName = createdByName ? parserNamesPool[0] : PARSER_NAME_UNKNOWN;
mPositionHolder = new PositionHolder();
mExoDataReader = new InputReadingDataReader();
removePendingSeek();
@@ -1286,6 +1361,24 @@
mScratchParsableByteArrayAdapter = new ParsableByteArrayAdapter();
mSchemeInitDataConstructor = getSchemeInitDataConstructor();
mMuxedCaptionFormats = new ArrayList<>();
+
+ // MediaMetrics.
+ mTrackFormats = new SparseArray<>();
+ mLastObservedExceptionName = "";
+ mDurationMillis = -1;
+ }
+
+ private String buildMediaMetricsString(Function<Format, String> formatFieldGetter) {
+ StringBuilder stringBuilder = new StringBuilder();
+ for (int i = 0; i < mTrackFormats.size(); i++) {
+ if (i > 0) {
+ stringBuilder.append(MEDIAMETRICS_ELEMENT_SEPARATOR);
+ }
+ String fieldValue = formatFieldGetter.apply(mTrackFormats.valueAt(i));
+ stringBuilder.append(fieldValue != null ? fieldValue : "");
+ }
+ return stringBuilder.substring(
+ 0, Math.min(stringBuilder.length(), MEDIAMETRICS_MAX_STRING_SIZE));
}
private void setMuxedCaptionFormats(List<MediaFormat> mediaFormats) {
@@ -1528,6 +1621,10 @@
@Override
public void seekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
+ long durationUs = exoplayerSeekMap.getDurationUs();
+ if (durationUs != C.TIME_UNSET) {
+ mDurationMillis = C.usToMs(durationUs);
+ }
if (mExposeChunkIndexAsMediaFormat && exoplayerSeekMap instanceof ChunkIndex) {
ChunkIndex chunkIndex = (ChunkIndex) exoplayerSeekMap;
MediaFormat mediaFormat = new MediaFormat();
@@ -1575,6 +1672,7 @@
@Override
public void format(Format format) {
+ mTrackFormats.put(mTrackIndex, format);
mOutputConsumer.onTrackDataFound(
mTrackIndex,
new TrackData(
@@ -2031,6 +2129,20 @@
return new SeekPoint(exoPlayerSeekPoint.timeUs, exoPlayerSeekPoint.position);
}
+ /**
+ * Introduces random error to the given metric value in order to prevent the identification of
+ * the parsed media.
+ */
+ private static long addDither(long value) {
+ // Generate a random in [0, 1].
+ double randomDither = ThreadLocalRandom.current().nextFloat();
+ // Clamp the random number to [0, 2 * MEDIAMETRICS_DITHER].
+ randomDither *= 2 * MEDIAMETRICS_DITHER;
+ // Translate the random number to [1 - MEDIAMETRICS_DITHER, 1 + MEDIAMETRICS_DITHER].
+ randomDither += 1 - MEDIAMETRICS_DITHER;
+ return value != -1 ? (long) (value * randomDither) : -1;
+ }
+
private static void assertValidNames(@NonNull String[] names) {
for (String name : names) {
if (!EXTRACTOR_FACTORIES_BY_NAME.containsKey(name)) {
@@ -2070,9 +2182,26 @@
}
}
+ // Native methods.
+
+ private native void nativeSubmitMetrics(
+ String parserName,
+ boolean createdByName,
+ String parserPool,
+ String lastObservedExceptionName,
+ long resourceByteCount,
+ long durationMillis,
+ String trackMimeTypes,
+ String trackCodecs,
+ String alteredParameters,
+ int videoWidth,
+ int videoHeight);
+
// Static initialization.
static {
+ System.loadLibrary(JNI_LIBRARY_NAME);
+
// Using a LinkedHashMap to keep the insertion order when iterating over the keys.
LinkedHashMap<String, ExtractorFactory> extractorFactoriesByName = new LinkedHashMap<>();
// Parsers are ordered to match ExoPlayer's DefaultExtractorsFactory extractor ordering,
@@ -2125,6 +2254,15 @@
// We do not check PARAMETER_EXPOSE_CAPTION_FORMATS here, and we do it in setParameters
// instead. Checking that the value is a List is insufficient to catch wrong parameter
// value types.
+ int sumOfParameterNameLengths =
+ expectedTypeByParameterName.keySet().stream()
+ .map(String::length)
+ .reduce(0, Integer::sum);
+ sumOfParameterNameLengths += PARAMETER_EXPOSE_CAPTION_FORMATS.length();
+ // Add space for any required separators.
+ MEDIAMETRICS_PARAMETER_LIST_MAX_LENGTH =
+ sumOfParameterNameLengths + expectedTypeByParameterName.size();
+
EXPECTED_TYPE_BY_PARAMETER_NAME = Collections.unmodifiableMap(expectedTypeByParameterName);
}
}
diff --git a/apex/media/framework/jni/android_media_MediaParserJNI.cpp b/apex/media/framework/jni/android_media_MediaParserJNI.cpp
new file mode 100644
index 0000000..7fc4628
--- /dev/null
+++ b/apex/media/framework/jni/android_media_MediaParserJNI.cpp
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#include <jni.h>
+#include <media/MediaMetrics.h>
+
+#define JNI_FUNCTION(RETURN_TYPE, NAME, ...) \
+ extern "C" { \
+ JNIEXPORT RETURN_TYPE Java_android_media_MediaParser_##NAME(JNIEnv* env, jobject thiz, \
+ ##__VA_ARGS__); \
+ } \
+ JNIEXPORT RETURN_TYPE Java_android_media_MediaParser_##NAME(JNIEnv* env, jobject thiz, \
+ ##__VA_ARGS__)
+
+namespace {
+
+constexpr char kMediaMetricsKey[] = "mediaparser";
+
+constexpr char kAttributeParserName[] = "android.media.mediaparser.parserName";
+constexpr char kAttributeCreatedByName[] = "android.media.mediaparser.createdByName";
+constexpr char kAttributeParserPool[] = "android.media.mediaparser.parserPool";
+constexpr char kAttributeLastException[] = "android.media.mediaparser.lastException";
+constexpr char kAttributeResourceByteCount[] = "android.media.mediaparser.resourceByteCount";
+constexpr char kAttributeDurationMillis[] = "android.media.mediaparser.durationMillis";
+constexpr char kAttributeTrackMimeTypes[] = "android.media.mediaparser.trackMimeTypes";
+constexpr char kAttributeTrackCodecs[] = "android.media.mediaparser.trackCodecs";
+constexpr char kAttributeAlteredParameters[] = "android.media.mediaparser.alteredParameters";
+constexpr char kAttributeVideoWidth[] = "android.media.mediaparser.videoWidth";
+constexpr char kAttributeVideoHeight[] = "android.media.mediaparser.videoHeight";
+
+// Util class to handle string resource management.
+class JstringHandle {
+public:
+ JstringHandle(JNIEnv* env, jstring value) : mEnv(env), mJstringValue(value) {
+ mCstringValue = env->GetStringUTFChars(value, /* isCopy= */ nullptr);
+ }
+
+ ~JstringHandle() {
+ if (mCstringValue != nullptr) {
+ mEnv->ReleaseStringUTFChars(mJstringValue, mCstringValue);
+ }
+ }
+
+ [[nodiscard]] const char* value() const {
+ return mCstringValue != nullptr ? mCstringValue : "";
+ }
+
+ JNIEnv* mEnv;
+ jstring mJstringValue;
+ const char* mCstringValue;
+};
+
+} // namespace
+
+JNI_FUNCTION(void, nativeSubmitMetrics, jstring parserNameJstring, jboolean createdByName,
+ jstring parserPoolJstring, jstring lastExceptionJstring, jlong resourceByteCount,
+ jlong durationMillis, jstring trackMimeTypesJstring, jstring trackCodecsJstring,
+ jstring alteredParameters, jint videoWidth, jint videoHeight) {
+ mediametrics_handle_t item(mediametrics_create(kMediaMetricsKey));
+ mediametrics_setCString(item, kAttributeParserName,
+ JstringHandle(env, parserNameJstring).value());
+ mediametrics_setInt32(item, kAttributeCreatedByName, createdByName ? 1 : 0);
+ mediametrics_setCString(item, kAttributeParserPool,
+ JstringHandle(env, parserPoolJstring).value());
+ mediametrics_setCString(item, kAttributeLastException,
+ JstringHandle(env, lastExceptionJstring).value());
+ mediametrics_setInt64(item, kAttributeResourceByteCount, resourceByteCount);
+ mediametrics_setInt64(item, kAttributeDurationMillis, durationMillis);
+ mediametrics_setCString(item, kAttributeTrackMimeTypes,
+ JstringHandle(env, trackMimeTypesJstring).value());
+ mediametrics_setCString(item, kAttributeTrackCodecs,
+ JstringHandle(env, trackCodecsJstring).value());
+ mediametrics_setCString(item, kAttributeAlteredParameters,
+ JstringHandle(env, alteredParameters).value());
+ mediametrics_setInt32(item, kAttributeVideoWidth, videoWidth);
+ mediametrics_setInt32(item, kAttributeVideoHeight, videoHeight);
+ mediametrics_selfRecord(item);
+ mediametrics_delete(item);
+}
diff --git a/api/current.txt b/api/current.txt
index 05f1027..b880f85 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -97,6 +97,7 @@
field public static final String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
field public static final String MANAGE_EXTERNAL_STORAGE = "android.permission.MANAGE_EXTERNAL_STORAGE";
+ field public static final String MANAGE_ONGOING_CALLS = "android.permission.MANAGE_ONGOING_CALLS";
field public static final String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
field public static final String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
field public static final String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
@@ -10820,7 +10821,6 @@
field public static final String EXTRA_REFERRER = "android.intent.extra.REFERRER";
field public static final String EXTRA_REFERRER_NAME = "android.intent.extra.REFERRER_NAME";
field public static final String EXTRA_REMOTE_INTENT_TOKEN = "android.intent.extra.remote_intent_token";
- field public static final String EXTRA_REMOVED_BY_SYSTEM = "android.intent.extra.REMOVED_BY_SYSTEM";
field public static final String EXTRA_REPLACEMENT_EXTRAS = "android.intent.extra.REPLACEMENT_EXTRAS";
field public static final String EXTRA_REPLACING = "android.intent.extra.REPLACING";
field public static final String EXTRA_RESTRICTIONS_BUNDLE = "android.intent.extra.restrictions_bundle";
@@ -10846,6 +10846,7 @@
field public static final String EXTRA_UID = "android.intent.extra.UID";
field public static final String EXTRA_UNSTARTABLE_REASON = "android.intent.extra.UNSTARTABLE_REASON";
field public static final String EXTRA_USER = "android.intent.extra.USER";
+ field public static final String EXTRA_USER_INITIATED = "android.intent.extra.USER_INITIATED";
field public static final int FILL_IN_ACTION = 1; // 0x1
field public static final int FILL_IN_CATEGORIES = 4; // 0x4
field public static final int FILL_IN_CLIP_DATA = 128; // 0x80
@@ -11718,7 +11719,10 @@
method public android.graphics.drawable.Drawable getIcon(int);
method public CharSequence getLabel();
method public String getName();
+ method public float getProgress();
method public android.os.UserHandle getUser();
+ method public boolean isLoading();
+ method public boolean isStartable();
}
public class LauncherApps {
@@ -11758,6 +11762,7 @@
ctor public LauncherApps.Callback();
method public abstract void onPackageAdded(String, android.os.UserHandle);
method public abstract void onPackageChanged(String, android.os.UserHandle);
+ method public void onPackageProgressChanged(@NonNull String, @NonNull android.os.UserHandle, float);
method public abstract void onPackageRemoved(String, android.os.UserHandle);
method public abstract void onPackagesAvailable(String[], android.os.UserHandle, boolean);
method public void onPackagesSuspended(String[], android.os.UserHandle);
@@ -12265,7 +12270,7 @@
field @Deprecated public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
field public static final int GET_GIDS = 256; // 0x100
field public static final int GET_INSTRUMENTATION = 16; // 0x10
- field public static final int GET_INTENT_FILTERS = 32; // 0x20
+ field @Deprecated public static final int GET_INTENT_FILTERS = 32; // 0x20
field public static final int GET_META_DATA = 128; // 0x80
field public static final int GET_PERMISSIONS = 4096; // 0x1000
field public static final int GET_PROVIDERS = 8; // 0x8
@@ -15453,6 +15458,7 @@
public class RadialGradient extends android.graphics.Shader {
ctor public RadialGradient(float, float, float, @ColorInt @NonNull int[], @Nullable float[], @NonNull android.graphics.Shader.TileMode);
ctor public RadialGradient(float, float, float, @ColorLong @NonNull long[], @Nullable float[], @NonNull android.graphics.Shader.TileMode);
+ ctor public RadialGradient(float, float, @FloatRange(from=0.0f) float, float, float, @FloatRange(from=0.0f, fromInclusive=false) float, @ColorLong @NonNull long[], @Nullable float[], @NonNull android.graphics.Shader.TileMode);
ctor public RadialGradient(float, float, float, @ColorInt int, @ColorInt int, @NonNull android.graphics.Shader.TileMode);
ctor public RadialGradient(float, float, float, @ColorLong long, @ColorLong long, @NonNull android.graphics.Shader.TileMode);
}
@@ -17859,6 +17865,7 @@
public class CaptureResult extends android.hardware.camera2.CameraMetadata<android.hardware.camera2.CaptureResult.Key<?>> {
method @Nullable public <T> T get(android.hardware.camera2.CaptureResult.Key<T>);
+ method @NonNull public String getCameraId();
method public long getFrameNumber();
method @NonNull public java.util.List<android.hardware.camera2.CaptureResult.Key<?>> getKeys();
method @NonNull public android.hardware.camera2.CaptureRequest getRequest();
@@ -31772,7 +31779,9 @@
method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setSsidPattern(@NonNull android.os.PatternMatcher);
method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa2EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa2Passphrase(@NonNull String);
- method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa3EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
+ method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa3Enterprise192BitModeConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
+ method @Deprecated @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa3EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
+ method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa3EnterpriseStandardModeConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa3Passphrase(@NonNull String);
}
@@ -31820,7 +31829,9 @@
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWapiPassphrase(@NonNull String);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa2EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa2Passphrase(@NonNull String);
- method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa3EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa3Enterprise192BitModeConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
+ method @Deprecated @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa3EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa3EnterpriseStandardModeConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa3Passphrase(@NonNull String);
}
@@ -36674,6 +36685,11 @@
ctor public OperationCanceledException(String);
}
+ public interface OutcomeReceiver<R, E extends java.lang.Throwable> {
+ method public default void onError(@NonNull E);
+ method public void onResult(@NonNull R);
+ }
+
public final class Parcel {
method public void appendFrom(android.os.Parcel, int, int);
method @Nullable public android.os.IBinder[] createBinderArray();
@@ -38868,6 +38884,9 @@
ctor public CallLog.Calls();
method public static String getLastOutgoingCall(android.content.Context);
field public static final int ANSWERED_EXTERNALLY_TYPE = 7; // 0x7
+ field public static final long AUTO_MISSED_EMERGENCY_CALL = 1L; // 0x1L
+ field public static final long AUTO_MISSED_MAXIMUM_DIALING = 4L; // 0x4L
+ field public static final long AUTO_MISSED_MAXIMUM_RINGING = 2L; // 0x2L
field public static final int BLOCKED_TYPE = 6; // 0x6
field public static final String BLOCK_REASON = "block_reason";
field public static final int BLOCK_REASON_BLOCKED_NUMBER = 3; // 0x3
@@ -38913,6 +38932,8 @@
field public static final String IS_READ = "is_read";
field public static final String LAST_MODIFIED = "last_modified";
field public static final String LIMIT_PARAM_KEY = "limit";
+ field public static final String MISSED_REASON = "missed_reason";
+ field public static final long MISSED_REASON_NOT_MISSED = 0L; // 0x0L
field public static final int MISSED_TYPE = 3; // 0x3
field public static final String NEW = "new";
field public static final String NUMBER = "number";
@@ -38929,6 +38950,13 @@
field public static final int REJECTED_TYPE = 5; // 0x5
field public static final String TRANSCRIPTION = "transcription";
field public static final String TYPE = "type";
+ field public static final long USER_MISSED_CALL_FILTERS_TIMEOUT = 4194304L; // 0x400000L
+ field public static final long USER_MISSED_CALL_SCREENING_SERVICE_SILENCED = 2097152L; // 0x200000L
+ field public static final long USER_MISSED_DND_MODE = 262144L; // 0x40000L
+ field public static final long USER_MISSED_LOW_RING_VOLUME = 524288L; // 0x80000L
+ field public static final long USER_MISSED_NO_ANSWER = 65536L; // 0x10000L
+ field public static final long USER_MISSED_NO_VIBRATE = 1048576L; // 0x100000L
+ field public static final long USER_MISSED_SHORT_RING = 131072L; // 0x20000L
field public static final String VIA_NUMBER = "via_number";
field public static final int VOICEMAIL_TYPE = 4; // 0x4
field public static final String VOICEMAIL_URI = "voicemail_uri";
@@ -43836,6 +43864,7 @@
field public static final int TYPE_RANGE = 2; // 0x2
field public static final int TYPE_STATELESS = 8; // 0x8
field public static final int TYPE_TEMPERATURE = 7; // 0x7
+ field public static final int TYPE_THUMBNAIL = 3; // 0x3
field public static final int TYPE_TOGGLE = 1; // 0x1
field public static final int TYPE_TOGGLE_RANGE = 6; // 0x6
}
@@ -43875,6 +43904,14 @@
field public static final int MODE_UNKNOWN = 0; // 0x0
}
+ public final class ThumbnailTemplate extends android.service.controls.templates.ControlTemplate {
+ ctor public ThumbnailTemplate(@NonNull String, boolean, @NonNull android.graphics.drawable.Icon, @NonNull CharSequence);
+ method @NonNull public CharSequence getContentDescription();
+ method public int getTemplateType();
+ method @NonNull public android.graphics.drawable.Icon getThumbnail();
+ method public boolean isActive();
+ }
+
public final class ToggleRangeTemplate extends android.service.controls.templates.ControlTemplate {
ctor public ToggleRangeTemplate(@NonNull String, @NonNull android.service.controls.templates.ControlButton, @NonNull android.service.controls.templates.RangeTemplate);
ctor public ToggleRangeTemplate(@NonNull String, boolean, @NonNull CharSequence, @NonNull android.service.controls.templates.RangeTemplate);
@@ -46472,6 +46509,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber(android.telecom.PhoneAccountHandle);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String, android.telecom.PhoneAccountHandle);
+ method public boolean hasCompanionInCallServiceAccess();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isInCall();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isInManagedCall();
method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
@@ -47869,6 +47907,7 @@
method @NonNull public java.util.List<java.lang.Integer> getAvailableServices();
method @Nullable public android.telephony.CellIdentity getCellIdentity();
method public int getDomain();
+ method public int getNrState();
method @Nullable public String getRegisteredPlmn();
method public int getTransportType();
method public boolean isRegistered();
@@ -53740,7 +53779,6 @@
}
public interface OnReceiveContentCallback<T extends android.view.View> {
- method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes(@NonNull T);
method public boolean onReceiveContent(@NonNull T, @NonNull android.view.OnReceiveContentCallback.Payload);
}
@@ -54318,7 +54356,7 @@
method @IdRes public int getNextFocusRightId();
method @IdRes public int getNextFocusUpId();
method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
- method @Nullable public android.view.OnReceiveContentCallback<? extends android.view.View> getOnReceiveContentCallback();
+ method @Nullable public String[] getOnReceiveContentMimeTypes();
method @ColorInt public int getOutlineAmbientShadowColor();
method public android.view.ViewOutlineProvider getOutlineProvider();
method @ColorInt public int getOutlineSpotShadowColor();
@@ -54513,6 +54551,7 @@
method public void onProvideContentCaptureStructure(@NonNull android.view.ViewStructure, int);
method public void onProvideStructure(android.view.ViewStructure);
method public void onProvideVirtualStructure(android.view.ViewStructure);
+ method public boolean onReceiveContent(@NonNull android.view.OnReceiveContentCallback.Payload);
method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int);
method @CallSuper protected void onRestoreInstanceState(android.os.Parcelable);
method public void onRtlPropertiesChanged(int);
@@ -54670,7 +54709,7 @@
method public void setOnHoverListener(android.view.View.OnHoverListener);
method public void setOnKeyListener(android.view.View.OnKeyListener);
method public void setOnLongClickListener(@Nullable android.view.View.OnLongClickListener);
- method public void setOnReceiveContentCallback(@Nullable android.view.OnReceiveContentCallback<? extends android.view.View>);
+ method public void setOnReceiveContentCallback(@Nullable String[], @Nullable android.view.OnReceiveContentCallback);
method public void setOnScrollChangeListener(android.view.View.OnScrollChangeListener);
method @Deprecated public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener);
method public void setOnTouchListener(android.view.View.OnTouchListener);
@@ -57413,6 +57452,7 @@
method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
method public android.os.Handler getHandler();
method public CharSequence getSelectedText(int);
+ method @Nullable public default android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
method public CharSequence getTextAfterCursor(int, int);
method public CharSequence getTextBeforeCursor(int, int);
method public boolean performContextMenuAction(int);
@@ -57621,6 +57661,17 @@
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeNameResId(int);
}
+ public final class SurroundingText implements android.os.Parcelable {
+ ctor public SurroundingText(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0xffffffff) int);
+ method public int describeContents();
+ method @IntRange(from=0xffffffff) public int getOffset();
+ method @IntRange(from=0) public int getSelectionEnd();
+ method @IntRange(from=0) public int getSelectionStart();
+ method @NonNull public CharSequence getText();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.SurroundingText> CREATOR;
+ }
+
}
package android.view.inspector {
@@ -61488,7 +61539,6 @@
method public int getMinWidth();
method public final android.text.method.MovementMethod getMovementMethod();
method public int getOffsetForPosition(float, float);
- method @Nullable public android.view.OnReceiveContentCallback<android.widget.TextView> getOnReceiveContentCallback();
method public android.text.TextPaint getPaint();
method public int getPaintFlags();
method public String getPrivateImeOptions();
@@ -61677,7 +61727,6 @@
public class TextViewOnReceiveContentCallback implements android.view.OnReceiveContentCallback<android.widget.TextView> {
ctor public TextViewOnReceiveContentCallback();
- method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes(@NonNull android.widget.TextView);
method public boolean onReceiveContent(@NonNull android.widget.TextView, @NonNull android.view.OnReceiveContentCallback.Payload);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 7305e28..c932718 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -310,6 +310,7 @@
field public static final int config_helpPackageNameKey = 17039387; // 0x104001b
field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
+ field public static final int config_systemAutomotiveProjection = 17039402; // 0x104002a
field public static final int config_systemGallery = 17039399; // 0x1040027
field public static final int config_systemVideoCall = 17039401; // 0x1040029
}
@@ -402,6 +403,7 @@
field public static final String OPSTR_LOADER_USAGE_STATS = "android:loader_usage_stats";
field public static final String OPSTR_MANAGE_EXTERNAL_STORAGE = "android:manage_external_storage";
field public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
+ field public static final String OPSTR_MANAGE_ONGOING_CALLS = "android:manage_ongoing_calls";
field public static final String OPSTR_MUTE_MICROPHONE = "android:mute_microphone";
field public static final String OPSTR_NEIGHBORING_CELLS = "android:neighboring_cells";
field public static final String OPSTR_PLAY_AUDIO = "android:play_audio";
@@ -916,7 +918,6 @@
field public static final int PROVISIONING_TRIGGER_QR_CODE = 2; // 0x2
field public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0; // 0x0
field public static final int STATE_USER_PROFILE_COMPLETE = 4; // 0x4
- field public static final int STATE_USER_PROFILE_FINALIZED = 5; // 0x5
field public static final int STATE_USER_SETUP_COMPLETE = 2; // 0x2
field public static final int STATE_USER_SETUP_FINALIZED = 3; // 0x3
field public static final int STATE_USER_SETUP_INCOMPLETE = 1; // 0x1
@@ -4428,35 +4429,13 @@
}
public final class MediaTranscodeManager {
- method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException, android.media.MediaTranscodingException.ServiceNotAvailableException;
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingSession enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException, android.media.MediaTranscodingException.ServiceNotAvailableException;
field public static final int PRIORITY_REALTIME = 1; // 0x1
field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1
}
@java.lang.FunctionalInterface public static interface MediaTranscodeManager.OnTranscodingFinishedListener {
- method public void onTranscodingFinished(@NonNull android.media.MediaTranscodeManager.TranscodingJob);
- }
-
- public static final class MediaTranscodeManager.TranscodingJob {
- method public void cancel();
- method public int getJobId();
- method @IntRange(from=0, to=100) public int getProgress();
- method public int getResult();
- method public int getStatus();
- method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
- method public void setOnProgressUpdateListener(int, @NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
- field public static final int RESULT_CANCELED = 4; // 0x4
- field public static final int RESULT_ERROR = 3; // 0x3
- field public static final int RESULT_NONE = 1; // 0x1
- field public static final int RESULT_SUCCESS = 2; // 0x2
- field public static final int STATUS_FINISHED = 3; // 0x3
- field public static final int STATUS_PAUSED = 4; // 0x4
- field public static final int STATUS_PENDING = 1; // 0x1
- field public static final int STATUS_RUNNING = 2; // 0x2
- }
-
- @java.lang.FunctionalInterface public static interface MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener {
- method public void onProgressUpdate(@NonNull android.media.MediaTranscodeManager.TranscodingJob, @IntRange(from=0, to=100) int);
+ method public void onTranscodingFinished(@NonNull android.media.MediaTranscodeManager.TranscodingSession);
}
public static final class MediaTranscodeManager.TranscodingRequest {
@@ -4489,6 +4468,28 @@
field public static final String CAPS_SUPPORTS_HEVC = "support-hevc";
}
+ public static final class MediaTranscodeManager.TranscodingSession {
+ method public void cancel();
+ method @IntRange(from=0, to=100) public int getProgress();
+ method public int getResult();
+ method public int getSessionId();
+ method public int getStatus();
+ method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingSession.OnProgressUpdateListener);
+ method public void setOnProgressUpdateListener(int, @NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingSession.OnProgressUpdateListener);
+ field public static final int RESULT_CANCELED = 4; // 0x4
+ field public static final int RESULT_ERROR = 3; // 0x3
+ field public static final int RESULT_NONE = 1; // 0x1
+ field public static final int RESULT_SUCCESS = 2; // 0x2
+ field public static final int STATUS_FINISHED = 3; // 0x3
+ field public static final int STATUS_PAUSED = 4; // 0x4
+ field public static final int STATUS_PENDING = 1; // 0x1
+ field public static final int STATUS_RUNNING = 2; // 0x2
+ }
+
+ @java.lang.FunctionalInterface public static interface MediaTranscodeManager.TranscodingSession.OnProgressUpdateListener {
+ method public void onProgressUpdate(@NonNull android.media.MediaTranscodeManager.TranscodingSession, @IntRange(from=0, to=100) int);
+ }
+
public class PlayerProxy {
method public void pause();
method public void setPan(float);
@@ -4935,6 +4936,7 @@
}
public abstract class TvInputService extends android.app.Service {
+ method @Nullable public android.os.IBinder createExtension();
method @Nullable public android.media.tv.TvInputInfo onHardwareAdded(android.media.tv.TvInputHardwareInfo);
method @Nullable public String onHardwareRemoved(android.media.tv.TvInputHardwareInfo);
method @Nullable public android.media.tv.TvInputInfo onHdmiDeviceAdded(android.hardware.hdmi.HdmiDeviceInfo);
@@ -6693,6 +6695,7 @@
method @NonNull public int[] getTransportTypes();
method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
+ field public static final int NET_CAPABILITY_OEM_PRIVATE = 26; // 0x1a
field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
}
@@ -7500,7 +7503,7 @@
field public static final int BAND_2GHZ = 1; // 0x1
field public static final int BAND_5GHZ = 2; // 0x2
field public static final int BAND_6GHZ = 4; // 0x4
- field public static final int BAND_ANY = 7; // 0x7
+ field @Deprecated public static final int BAND_ANY = 7; // 0x7
field public static final int RANDOMIZATION_NONE = 0; // 0x0
field public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1
}
@@ -11802,6 +11805,7 @@
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
+ method public boolean isNrDualConnectivityEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
@@ -11816,6 +11820,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestModemActivityInfo(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.ModemActivityInfo,android.telephony.TelephonyManager.ModemActivityInfoException>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetAllCarrierActions();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetCarrierKeysForImsiEncryption();
@@ -11834,6 +11839,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
+ method public int setNrDualConnectivityState(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean);
@@ -11873,6 +11879,11 @@
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
+ field public static final int ENABLE_NR_DUAL_CONNECTIVITY_INVALID_STATE = 4; // 0x4
+ field public static final int ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED = 1; // 0x1
+ field public static final int ENABLE_NR_DUAL_CONNECTIVITY_RADIO_ERROR = 3; // 0x3
+ field public static final int ENABLE_NR_DUAL_CONNECTIVITY_RADIO_NOT_AVAILABLE = 2; // 0x2
+ field public static final int ENABLE_NR_DUAL_CONNECTIVITY_SUCCESS = 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 public static final String EXTRA_PHONE_IN_ECM_STATE = "android.telephony.extra.PHONE_IN_ECM_STATE";
@@ -11905,6 +11916,9 @@
field public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = 65536L; // 0x10000L
field public static final long NETWORK_TYPE_BITMASK_UMTS = 4L; // 0x4L
field public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; // 0x0L
+ field public static final int NR_DUAL_CONNECTIVITY_DISABLE = 2; // 0x2
+ field public static final int NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE = 3; // 0x3
+ field public static final int NR_DUAL_CONNECTIVITY_ENABLE = 1; // 0x1
field public static final int RADIO_POWER_OFF = 0; // 0x0
field public static final int RADIO_POWER_ON = 1; // 0x1
field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2
@@ -11934,6 +11948,14 @@
field public static final int RESULT_SUCCESS = 0; // 0x0
}
+ public static class TelephonyManager.ModemActivityInfoException extends java.lang.Exception {
+ method public int getErrorCode();
+ field public static final int ERROR_INVALID_INFO_RECEIVED = 2; // 0x2
+ field public static final int ERROR_MODEM_RESPONSE_ERROR = 3; // 0x3
+ field public static final int ERROR_PHONE_NOT_AVAILABLE = 1; // 0x1
+ field public static final int ERROR_UNKNOWN = 0; // 0x0
+ }
+
public final class UiccAccessRule implements android.os.Parcelable {
ctor public UiccAccessRule(byte[], @Nullable String, long);
method public int describeContents();
diff --git a/api/test-current.txt b/api/test-current.txt
index 8bf8c64..82838ea 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -46,6 +46,7 @@
field public static final int config_defaultAssistant = 17039393; // 0x1040021
field public static final int config_defaultDialer = 17039395; // 0x1040023
field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
+ field public static final int config_systemAutomotiveProjection = 17039402; // 0x104002a
field public static final int config_systemGallery = 17039399; // 0x1040027
field public static final int config_systemVideoCall = 17039401; // 0x1040029
}
@@ -83,7 +84,7 @@
method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
method public void alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName);
method public long getTotalRam();
- method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public void holdLock(int);
+ method public void holdLock(android.os.IBinder, int);
method public static boolean isHighEndGfx();
method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);
method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors();
@@ -209,6 +210,7 @@
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_MANAGE_ONGOING_CALLS = "android:manage_ongoing_calls";
field public static final int OP_COARSE_LOCATION = 0; // 0x0
field public static final int OP_RECORD_AUDIO = 27; // 0x1b
field public static final int OP_START_FOREGROUND = 76; // 0x4c
@@ -532,6 +534,7 @@
public abstract class PackageManager {
method @Nullable public String getContentCaptureServicePackageName();
method @Nullable public String getDefaultTextClassifierPackageName();
+ method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public android.os.IBinder getHoldLockToken();
method public abstract int getInstallReason(@NonNull String, @NonNull android.os.UserHandle);
method @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
method @Nullable public abstract String[] getNamesForUids(int[]);
@@ -540,7 +543,7 @@
method @NonNull public abstract String getSharedSystemSharedLibraryPackageName();
method @Nullable public String getSystemTextClassifierPackageName();
method @Nullable public String getWellbeingPackageName();
- method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public void holdLock(int);
+ method public void holdLock(android.os.IBinder, int);
field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80
@@ -1733,6 +1736,7 @@
public final class ModemActivityInfo implements android.os.Parcelable {
ctor public ModemActivityInfo(long, int, int, @NonNull int[], int);
+ method public boolean isEmpty();
method public boolean isValid();
}
@@ -2033,7 +2037,7 @@
}
public interface WindowManager extends android.view.ViewManager {
- method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public default void holdLock(int);
+ method public default void holdLock(android.os.IBinder, int);
method public default void setShouldShowIme(int, boolean);
method public default void setShouldShowSystemDecors(int, boolean);
method public default void setShouldShowWithInsecureKeyguard(int, boolean);
@@ -2372,7 +2376,7 @@
public class TaskOrganizer extends android.window.WindowOrganizer {
ctor public TaskOrganizer();
- method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public android.window.TaskAppearedInfo createRootTask(int, int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void createRootTask(int, int, @Nullable android.os.IBinder);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean deleteRootTask(@NonNull android.window.WindowContainerToken);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getChildTasks(@NonNull android.window.WindowContainerToken, @NonNull int[]);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public android.window.WindowContainerToken getImeTarget(int);
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index 0440d1a..c0b4093 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -539,6 +539,8 @@
KotlinOperator: android.os.WorkSource#get(int):
+KotlinOperator: android.util.SparseArrayMap#get(int, K):
+
KotlinOperator: android.util.SparseArrayMap#get(int, String):
@@ -594,17 +596,17 @@
MissingGetterMatchingBuilder: android.app.ActivityView.Builder#setAttributeSet(android.util.AttributeSet):
- android.app.ActivityView does not declare a `getAttributeSet()` method matching method android.app.ActivityView.Builder.setAttributeSet(android.util.AttributeSet)
+
MissingGetterMatchingBuilder: android.app.ActivityView.Builder#setDefaultStyle(int):
- android.app.ActivityView does not declare a `getDefaultStyle()` method matching method android.app.ActivityView.Builder.setDefaultStyle(int)
+
MissingGetterMatchingBuilder: android.app.ActivityView.Builder#setDisableSurfaceViewBackgroundLayer(boolean):
- android.app.ActivityView does not declare a `isDisableSurfaceViewBackgroundLayer()` method matching method android.app.ActivityView.Builder.setDisableSurfaceViewBackgroundLayer(boolean)
+
MissingGetterMatchingBuilder: android.app.ActivityView.Builder#setSingleInstance(boolean):
- android.app.ActivityView does not declare a `isSingleInstance()` method matching method android.app.ActivityView.Builder.setSingleInstance(boolean)
+
MissingGetterMatchingBuilder: android.app.ActivityView.Builder#setUsePublicVirtualDisplay(boolean):
- android.app.ActivityView does not declare a `isUsePublicVirtualDisplay()` method matching method android.app.ActivityView.Builder.setUsePublicVirtualDisplay(boolean)
+
MissingGetterMatchingBuilder: android.app.ActivityView.Builder#setUseTrustedDisplay(boolean):
- android.app.ActivityView does not declare a `isUseTrustedDisplay()` method matching method android.app.ActivityView.Builder.setUseTrustedDisplay(boolean)
+
MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setAttributionTag(String):
MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setFlags(int):
@@ -751,6 +753,8 @@
MissingNullability: android.app.ActivityManager#getPackageImportance(String) parameter #0:
+MissingNullability: android.app.ActivityManager#holdLock(android.os.IBinder, int) parameter #0:
+
MissingNullability: android.app.ActivityManager#removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener) parameter #0:
MissingNullability: android.app.ActivityManager#scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int) parameter #0:
@@ -935,8 +939,12 @@
MissingNullability: android.content.pm.PackageInstaller.SessionParams#setGrantedRuntimePermissions(String[]) parameter #0:
+MissingNullability: android.content.pm.PackageManager#getHoldLockToken():
+ Missing nullability on method `BINDER` return
MissingNullability: android.content.pm.PackageManager#getNamesForUids(int[]) parameter #0:
+MissingNullability: android.content.pm.PackageManager#holdLock(android.os.IBinder, int) parameter #0:
+
MissingNullability: android.content.pm.ShortcutManager#ShortcutManager(android.content.Context) parameter #0:
MissingNullability: android.content.res.AssetManager#getOverlayablesToString(String) parameter #0:
@@ -2313,6 +2321,8 @@
MissingNullability: android.view.ViewDebug#startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.function.Function<android.graphics.Picture,java.lang.Boolean>) parameter #2:
+MissingNullability: android.view.WindowManager#holdLock(android.os.IBinder, int) parameter #0:
+
MissingNullability: android.view.WindowManager.LayoutParams#accessibilityTitle:
MissingNullability: android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener#onAccessibilityServicesStateChanged(android.view.accessibility.AccessibilityManager) parameter #0:
@@ -2893,6 +2903,10 @@
+StartWithLower: android.content.pm.PackageManager#BINDER():
+ Method name must start with lowercase char: BINDER
+
+
StaticFinalBuilder: android.content.integrity.RuleSet.Builder:
StaticFinalBuilder: android.hardware.display.BrightnessConfiguration.Builder:
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 3d67e65..1cfe9ee 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -59,6 +59,7 @@
import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto";
import "frameworks/base/core/proto/android/stats/style/style_enums.proto";
import "frameworks/base/core/proto/android/stats/sysui/notification_enums.proto";
+import "frameworks/base/core/proto/android/stats/tls/enums.proto";
import "frameworks/base/core/proto/android/telecomm/enums.proto";
import "frameworks/base/core/proto/android/telephony/enums.proto";
import "frameworks/base/core/proto/android/view/enums.proto";
@@ -495,9 +496,11 @@
HdmiCecMessageReported hdmi_cec_message_reported = 310 [(module) = "framework"];
AirplaneMode airplane_mode = 311 [(module) = "telephony"];
ModemRestart modem_restart = 312 [(module) = "telephony"];
- CarrierIdMismatchEvent carrier_id_mismatch_event = 313 [(module) = "telephony"];
- CarrierIdMatchingTable carrier_id_table_update = 314 [(module) = "telephony"];
+ CarrierIdMismatchReported carrier_id_mismatch_reported = 313 [(module) = "telephony"];
+ CarrierIdTableUpdated carrier_id_table_updated = 314 [(module) = "telephony"];
DataStallRecoveryReported data_stall_recovery_reported = 315 [(module) = "telephony"];
+ MediametricsMediaParserReported mediametrics_mediaparser_reported = 316;
+ TlsHandshakeReported tls_handshake_reported = 317 [(module) = "conscrypt"];
// StatsdStats tracks platform atoms with ids upto 500.
// Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
@@ -605,7 +608,7 @@
10085 [(module) = "mediaprovider"];
IncomingSms incoming_sms = 10086 [(module) = "telephony"];
OutgoingSms outgoing_sms = 10087 [(module) = "telephony"];
- CarrierIdMatchingTable carrier_id_table_version = 10088 [(module) = "telephony"];
+ CarrierIdTableVersion carrier_id_table_version = 10088 [(module) = "telephony"];
DataCallSession data_call_session = 10089 [(module) = "telephony"];
CellularServiceState cellular_service_state = 10090 [(module) = "telephony"];
CellularDataServiceSwitch cellular_data_service_switch = 10091 [(module) = "telephony"];
@@ -3361,6 +3364,7 @@
optional int32 color_preference = 9;
optional android.stats.style.LocationPreference location_preference = 10;
optional android.stats.style.DatePreference date_preference = 11;
+ optional android.stats.style.LaunchedPreference launched_preference = 12;
}
/**
@@ -4658,7 +4662,7 @@
UNKNOWN = 0;
CHIP_VIEWED = 1;
CHIP_CLICKED = 2;
- reserved 3; // Used only in beta builds, never shipped
+ reserved 3; // Used only in beta builds, never shipped
DIALOG_DISMISS = 4;
DIALOG_LINE_ITEM = 5;
}
@@ -8196,6 +8200,72 @@
}
/**
+ * Track MediaParser (parsing video/audio streams from containers) usage
+ * Logged from:
+ *
+ * frameworks/av/services/mediametrics/statsd_mediaparser.cpp
+ * frameworks/base/apex/media/framework/jni/android_media_MediaParserJNI.cpp
+ */
+message MediametricsMediaParserReported {
+ optional int64 timestamp_nanos = 1;
+ optional string package_name = 2;
+ optional int64 package_version_code = 3;
+
+ // MediaParser specific data.
+ /**
+ * The name of the parser selected for parsing the media, or an empty string
+ * if no parser was selected.
+ */
+ optional string parser_name = 4;
+ /**
+ * Whether the parser was created by name. 1 represents true, and 0
+ * represents false.
+ */
+ optional int32 created_by_name = 5;
+ /**
+ * The parser names in the sniffing pool separated by "|".
+ */
+ optional string parser_pool = 6;
+ /**
+ * The fully qualified name of the last encountered exception, or an empty
+ * string if no exception was encountered.
+ */
+ optional string last_exception = 7;
+ /**
+ * The size of the parsed media in bytes, or -1 if unknown. Note this value
+ * contains intentional random error to prevent media content
+ * identification.
+ */
+ optional int64 resource_byte_count = 8;
+ /**
+ * The duration of the media in milliseconds, or -1 if unknown. Note this
+ * value contains intentional random error to prevent media content
+ * identification.
+ */
+ optional int64 duration_millis = 9;
+ /**
+ * The MIME types of the tracks separated by "|".
+ */
+ optional string track_mime_types = 10;
+ /**
+ * The tracks' RFC 6381 codec strings separated by "|".
+ */
+ optional string track_codecs = 11;
+ /**
+ * Concatenation of the parameters altered by the client, separated by "|".
+ */
+ optional string altered_parameters = 12;
+ /**
+ * The video width in pixels, or -1 if unknown or not applicable.
+ */
+ optional int32 video_width = 13;
+ /**
+ * The video height in pixels, or -1 if unknown or not applicable.
+ */
+ optional int32 video_height = 14;
+}
+
+/**
* Track how we arbitrate between microphone/input requests.
* Logged from
* frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -10678,7 +10748,7 @@
}
/**
- * Push information about usage of airplane mode.
+ * Logs information about usage of airplane mode.
*
* Logged from:
* frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/AirplaneModeStats.java
@@ -10697,7 +10767,7 @@
}
/**
- * Push information about modem restarts.
+ * Logs information about modem restarts.
*
* Logged from:
* frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/ModemRestartStats.java
@@ -10715,7 +10785,7 @@
}
/**
- * Push the SIM card details when the carrier ID match is not complete.
+ * Logs the SIM card details when the carrier ID match is not complete.
*
* The atom is pushed when a SIM card is initialized and the MCC/MNC is not present in the
* carrier ID table, or the SIM card contains a GID1 value that is not present in the carrier ID
@@ -10724,7 +10794,7 @@
* Logged from:
* frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/CarrierIdMatchStats.java
*/
-message CarrierIdMismatchEvent {
+message CarrierIdMismatchReported {
// Matched carrier ID. The value -1 is used if no match is found.
optional int32 carrier_id = 1;
@@ -10736,17 +10806,30 @@
// SPN value of the SIM card.
optional string spn = 4;
+
+ // First record of the PNN in the SIM card. This field is populated only if the SPN is missing
+ // or empty.
+ optional string pnn = 5;
}
/**
- * Pulls/pushes the version of the carrier ID matching table.
- *
- * The atom is pushed when a new version is detected.
+ * Logs the version of the carrier ID matching table at first power up and when it is updated.
*
* Logged from:
* frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/CarrierIdMatchStats.java
*/
-message CarrierIdMatchingTable {
+message CarrierIdTableUpdated {
+ // Version of the CarrierId matching table.
+ optional int32 table_version = 1;
+}
+
+/**
+ * Pulls the version of the carrier ID matching table.
+ *
+ * Logged from:
+ * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+ */
+message CarrierIdTableVersion {
// Version of the CarrierId matching table.
optional int32 table_version = 1;
}
@@ -11887,3 +11970,19 @@
// The reason for the feature abort.
optional android.stats.hdmi.FeatureAbortReason feature_abort_reason = 9;
}
+
+/**
+ * Pushes TLS handshake counters from Conscrypt.
+ * Pulled from:
+ * external/conscrypt/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java
+ * external/conscrypt/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java
+ */
+message TlsHandshakeReported {
+ optional bool success = 1;
+
+ optional android.stats.tls.Protocol protocol = 2;
+
+ optional android.stats.tls.CipherSuite cipher_suite = 3;
+
+ optional int32 handshake_duration_millis = 4;
+}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 3d57cfe..22fdf16 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -18,12 +18,14 @@
#include "Log.h"
#include "ValueMetricProducer.h"
-#include "../guardrail/StatsdStats.h"
-#include "../stats_log_util.h"
#include <limits.h>
#include <stdlib.h>
+#include "../guardrail/StatsdStats.h"
+#include "../stats_log_util.h"
+#include "metrics/parsing_utils/metrics_manager_util.h"
+
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_DOUBLE;
@@ -184,6 +186,48 @@
}
}
+bool ValueMetricProducer::onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ if (!MetricProducer::onConfigUpdatedLocked(
+ config, configIndex, metricIndex, allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+ allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
+ trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+ return false;
+ }
+
+ const ValueMetric& metric = config.value_metric(configIndex);
+ // Update appropriate indices: mWhatMatcherIndex, mConditionIndex and MetricsManager maps.
+ if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, /*enforceOneAtom=*/false,
+ allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+ trackerToMetricMap, mWhatMatcherIndex)) {
+ return false;
+ }
+
+ if (metric.has_condition() &&
+ !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, mConditionTrackerIndex,
+ conditionToMetricMap)) {
+ return false;
+ }
+ sp<EventMatcherWizard> tmpEventWizard = mEventMatcherWizard;
+ mEventMatcherWizard = matcherWizard;
+ return true;
+}
+
void ValueMetricProducer::onStateChanged(int64_t eventTimeNs, int32_t atomId,
const HashableDimensionKey& primaryKey,
const FieldValue& oldState, const FieldValue& newState) {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 67de214..ebd8fec 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -47,7 +47,7 @@
// - a condition change
// - an app upgrade
// - an alarm set to the end of the bucket
-class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
+class ValueMetricProducer : public MetricProducer, public virtual PullDataReceiver {
public:
ValueMetricProducer(
const ConfigKey& key, const ValueMetric& valueMetric, const int conditionIndex,
@@ -155,7 +155,23 @@
// causes the bucket to be invalidated will not notify StatsdStats.
void skipCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason);
- const int mWhatMatcherIndex;
+ bool onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation) override;
+
+ int mWhatMatcherIndex;
sp<EventMatcherWizard> mEventMatcherWizard;
@@ -370,6 +386,8 @@
FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValue);
FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse);
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateValueMetrics);
+
friend class ValueMetricProducerTestHelper;
};
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
index cfc6e3f..335f775 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
@@ -546,7 +546,21 @@
return false;
}
}
-
+ for (int i = 0; i < config.value_metric_size(); i++, metricIndex++) {
+ const ValueMetric& metric = config.value_metric(i);
+ set<int64_t> conditionDependencies;
+ if (metric.has_condition()) {
+ conditionDependencies.insert(metric.condition());
+ }
+ if (!determineMetricUpdateStatus(
+ config, metric, metric.id(), METRIC_TYPE_VALUE, {metric.what()},
+ conditionDependencies, metric.slice_by_state(), metric.links(),
+ oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ replacedMatchers, replacedConditions, replacedStates,
+ metricsToUpdate[metricIndex])) {
+ return false;
+ }
+ }
for (int i = 0; i < config.gauge_metric_size(); i++, metricIndex++) {
const GaugeMetric& metric = config.gauge_metric(i);
set<int64_t> conditionDependencies;
@@ -566,7 +580,6 @@
return false;
}
}
- // TODO: determine update status for value metrics.
return true;
}
@@ -630,7 +643,7 @@
set<int64_t>& noReportMetricIds,
unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation) {
+ vector<int>& metricsWithActivation, set<int64_t>& replacedMetrics) {
sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchingTrackers);
const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
@@ -676,6 +689,8 @@
break;
}
case UPDATE_REPLACE:
+ replacedMetrics.insert(metric.id());
+ [[fallthrough]]; // Intentionally fallthrough to create the new metric producer.
case UPDATE_NEW: {
producer = createCountMetricProducerAndUpdateMetadata(
key, config, timeBaseNs, currentTimeNs, metric, metricIndex,
@@ -713,6 +728,8 @@
break;
}
case UPDATE_REPLACE:
+ replacedMetrics.insert(metric.id());
+ [[fallthrough]]; // Intentionally fallthrough to create the new metric producer.
case UPDATE_NEW: {
producer = createDurationMetricProducerAndUpdateMetadata(
key, config, timeBaseNs, currentTimeNs, metric, metricIndex,
@@ -735,8 +752,8 @@
newMetricProducers.push_back(producer.value());
}
for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
- newMetricProducerMap[config.event_metric(i).id()] = metricIndex;
const EventMetric& metric = config.event_metric(i);
+ newMetricProducerMap[metric.id()] = metricIndex;
optional<sp<MetricProducer>> producer;
switch (metricsToUpdate[metricIndex]) {
case UPDATE_PRESERVE: {
@@ -750,6 +767,8 @@
break;
}
case UPDATE_REPLACE:
+ replacedMetrics.insert(metric.id());
+ [[fallthrough]]; // Intentionally fallthrough to create the new metric producer.
case UPDATE_NEW: {
producer = createEventMetricProducerAndUpdateMetadata(
key, config, timeBaseNs, metric, metricIndex, allAtomMatchingTrackers,
@@ -770,6 +789,47 @@
}
newMetricProducers.push_back(producer.value());
}
+
+ for (int i = 0; i < config.value_metric_size(); i++, metricIndex++) {
+ const ValueMetric& metric = config.value_metric(i);
+ newMetricProducerMap[metric.id()] = metricIndex;
+ optional<sp<MetricProducer>> producer;
+ switch (metricsToUpdate[metricIndex]) {
+ case UPDATE_PRESERVE: {
+ producer = updateMetric(
+ config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+ allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+ oldMetricProducers, metricToActivationMap, trackerToMetricMap,
+ conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation);
+ break;
+ }
+ case UPDATE_REPLACE:
+ replacedMetrics.insert(metric.id());
+ [[fallthrough]]; // Intentionally fallthrough to create the new metric producer.
+ case UPDATE_NEW: {
+ producer = createValueMetricProducerAndUpdateMetadata(
+ key, config, timeBaseNs, currentTimeNs, pullerManager, metric, metricIndex,
+ allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
+ conditionTrackerMap, initialConditionCache, wizard, matcherWizard,
+ stateAtomIdMap, allStateGroupMaps, metricToActivationMap,
+ trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation);
+ break;
+ }
+ default: {
+ ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
+ (long long)metric.id());
+ return false;
+ }
+ }
+ if (!producer) {
+ return false;
+ }
+ newMetricProducers.push_back(producer.value());
+ }
+
for (int i = 0; i < config.gauge_metric_size(); i++, metricIndex++) {
const GaugeMetric& metric = config.gauge_metric(i);
newMetricProducerMap[metric.id()] = metricIndex;
@@ -786,6 +846,8 @@
break;
}
case UPDATE_REPLACE:
+ replacedMetrics.insert(metric.id());
+ [[fallthrough]]; // Intentionally fallthrough to create the new metric producer.
case UPDATE_NEW: {
producer = createGaugeMetricProducerAndUpdateMetadata(
key, config, timeBaseNs, currentTimeNs, pullerManager, metric, metricIndex,
@@ -807,7 +869,6 @@
}
newMetricProducers.push_back(producer.value());
}
- // TODO: perform update for value metric.
const set<int> atomsAllowedFromAnyUid(config.whitelisted_atom_ids().begin(),
config.whitelisted_atom_ids().end());
@@ -858,6 +919,7 @@
set<int64_t>& noReportMetricIds) {
set<int64_t> replacedMatchers;
set<int64_t> replacedConditions;
+ set<int64_t> replacedMetrics;
vector<ConditionState> conditionCache;
unordered_map<int64_t, int> stateAtomIdMap;
unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
@@ -899,7 +961,7 @@
replacedStates, oldMetricProducerMap, oldMetricProducers,
newMetricProducerMap, newMetricProducers, conditionToMetricMap,
trackerToMetricMap, noReportMetricIds, activationTrackerToMetricMap,
- deactivationTrackerToMetricMap, metricsWithActivation)) {
+ deactivationTrackerToMetricMap, metricsWithActivation, replacedMetrics)) {
ALOGE("initMetricProducers failed");
return false;
}
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
index 34d7e9c..3f1c532 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
@@ -187,7 +187,7 @@
std::set<int64_t>& noReportMetricIds,
std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation);
+ std::vector<int>& metricsWithActivation, std::set<int64_t>& replacedMetrics);
// Updates the existing MetricsManager from a new StatsdConfig.
// Parameters are the members of MetricsManager. See MetricsManager for declaration.
diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
index b7dc2c7..8fc039a 100644
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
@@ -599,6 +599,108 @@
eventDeactivationMap)};
}
+optional<sp<MetricProducer>> createValueMetricProducerAndUpdateMetadata(
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const ValueMetric& metric, const int metricIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap,
+ const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const unordered_map<int64_t, int>& stateAtomIdMap,
+ const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ if (!metric.has_id() || !metric.has_what()) {
+ ALOGE("cannot find metric id or \"what\" in ValueMetric \"%lld\"", (long long)metric.id());
+ return nullopt;
+ }
+ if (!metric.has_value_field()) {
+ ALOGE("cannot find \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
+ return nullopt;
+ }
+ std::vector<Matcher> fieldMatchers;
+ translateFieldMatcher(metric.value_field(), &fieldMatchers);
+ if (fieldMatchers.size() < 1) {
+ ALOGE("incorrect \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
+ return nullopt;
+ }
+
+ int trackerIndex;
+ if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
+ metric.has_dimensions_in_what(),
+ allAtomMatchingTrackers, atomMatchingTrackerMap,
+ trackerToMetricMap, trackerIndex)) {
+ return nullopt;
+ }
+
+ sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
+ // If it is pulled atom, it should be simple matcher with one tagId.
+ if (atomMatcher->getAtomIds().size() != 1) {
+ return nullopt;
+ }
+ int atomTagId = *(atomMatcher->getAtomIds().begin());
+ int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
+
+ int conditionIndex = -1;
+ if (metric.has_condition()) {
+ if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, conditionIndex,
+ conditionToMetricMap)) {
+ return nullopt;
+ }
+ } else if (metric.links_size() > 0) {
+ ALOGE("metrics has a MetricConditionLink but doesn't have a condition");
+ return nullopt;
+ }
+
+ std::vector<int> slicedStateAtoms;
+ unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+ if (metric.slice_by_state_size() > 0) {
+ if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+ allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+ return nullopt;
+ }
+ } else if (metric.state_link_size() > 0) {
+ ALOGE("ValueMetric has a MetricStateLink but doesn't have a sliced state");
+ return nullopt;
+ }
+
+ // Check that all metric state links are a subset of dimensions_in_what fields.
+ std::vector<Matcher> dimensionsInWhat;
+ translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
+ for (const auto& stateLink : metric.state_link()) {
+ if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
+ return nullopt;
+ }
+ }
+
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
+ atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation,
+ eventActivationMap, eventDeactivationMap)) {
+ return nullopt;
+ }
+
+ uint64_t metricHash;
+ if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+ return nullopt;
+ }
+
+ return {new ValueMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+ metricHash, trackerIndex, matcherWizard, pullTagId, timeBaseNs,
+ currentTimeNs, pullerManager, eventActivationMap,
+ eventDeactivationMap, slicedStateAtoms, stateGroupMap)};
+}
+
optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata(
const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
@@ -911,97 +1013,20 @@
// build ValueMetricProducer
for (int i = 0; i < config.value_metric_size(); i++) {
- const ValueMetric& metric = config.value_metric(i);
- if (!metric.has_what()) {
- ALOGW("cannot find \"what\" in ValueMetric \"%lld\"", (long long)metric.id());
- return false;
- }
- if (!metric.has_value_field()) {
- ALOGW("cannot find \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
- return false;
- }
- std::vector<Matcher> fieldMatchers;
- translateFieldMatcher(metric.value_field(), &fieldMatchers);
- if (fieldMatchers.size() < 1) {
- ALOGW("incorrect \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
- return false;
- }
-
int metricIndex = allMetricProducers.size();
+ const ValueMetric& metric = config.value_metric(i);
metricMap.insert({metric.id(), metricIndex});
- int trackerIndex;
- if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
- metric.has_dimensions_in_what(),
- allAtomMatchingTrackers, atomMatchingTrackerMap,
- trackerToMetricMap, trackerIndex)) {
- return false;
- }
-
- sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
- // If it is pulled atom, it should be simple matcher with one tagId.
- if (atomMatcher->getAtomIds().size() != 1) {
- return false;
- }
- int atomTagId = *(atomMatcher->getAtomIds().begin());
- int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
-
- int conditionIndex = -1;
- if (metric.has_condition()) {
- bool good = handleMetricWithConditions(
- metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
- allConditionTrackers, conditionIndex, conditionToMetricMap);
- if (!good) {
- return false;
- }
- } else {
- if (metric.links_size() > 0) {
- ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
- return false;
- }
- }
-
- std::vector<int> slicedStateAtoms;
- unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
- if (metric.slice_by_state_size() > 0) {
- if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
- allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
- return false;
- }
- } else {
- if (metric.state_link_size() > 0) {
- ALOGW("ValueMetric has a MetricStateLink but doesn't have a sliced state");
- return false;
- }
- }
-
- // Check that all metric state links are a subset of dimensions_in_what fields.
- std::vector<Matcher> dimensionsInWhat;
- translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
- for (const auto& stateLink : metric.state_link()) {
- if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
- return false;
- }
- }
-
- unordered_map<int, shared_ptr<Activation>> eventActivationMap;
- unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
- bool success = handleMetricActivation(
- config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap,
+ optional<sp<MetricProducer>> producer = createValueMetricProducerAndUpdateMetadata(
+ key, config, timeBaseTimeNs, currentTimeNs, pullerManager, metric, metricIndex,
+ allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
+ conditionTrackerMap, initialConditionCache, wizard, matcherWizard, stateAtomIdMap,
+ allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, eventActivationMap, eventDeactivationMap);
- if (!success) return false;
-
- uint64_t metricHash;
- if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+ metricsWithActivation);
+ if (!producer) {
return false;
}
-
- sp<MetricProducer> valueProducer = new ValueMetricProducer(
- key, metric, conditionIndex, initialConditionCache, wizard, metricHash,
- trackerIndex, matcherWizard, pullTagId, timeBaseTimeNs, currentTimeNs,
- pullerManager, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
- stateGroupMap);
- allMetricProducers.push_back(valueProducer);
+ allMetricProducers.push_back(producer.value());
}
// Gauge metrics.
diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
index 6d1e6dd..e4585cd 100644
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
@@ -148,6 +148,27 @@
std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
std::vector<int>& metricsWithActivation);
+// Creates a CountMetricProducer and updates the vectors/maps used by MetricsManager with
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createValueMetricProducerAndUpdateMetadata(
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const ValueMetric& metric, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const std::unordered_map<int64_t, int>& stateAtomIdMap,
+ const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation);
+
// Creates a GaugeMetricProducer and updates the vectors/maps used by MetricsManager with
// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata(
diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
index dc951be..4fa9bf6 100644
--- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
+++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
@@ -29,6 +29,7 @@
#include "src/matchers/CombinationAtomMatchingTracker.h"
#include "src/metrics/DurationMetricProducer.h"
#include "src/metrics/GaugeMetricProducer.h"
+#include "src/metrics/ValueMetricProducer.h"
#include "src/metrics/parsing_utils/metrics_manager_util.h"
#include "tests/statsd_test_util.h"
@@ -170,6 +171,23 @@
}
return metric;
}
+
+ValueMetric createValueMetric(string name, const AtomMatcher& what, optional<int64_t> condition,
+ vector<int64_t> states) {
+ ValueMetric metric;
+ metric.set_id(StringToId(name));
+ metric.set_what(what.id());
+ metric.set_bucket(TEN_MINUTES);
+ metric.mutable_value_field()->set_field(what.simple_atom_matcher().atom_id());
+ metric.mutable_value_field()->add_child()->set_field(2);
+ if (condition) {
+ metric.set_condition(condition.value());
+ }
+ for (const int64_t state : states) {
+ metric.add_slice_by_state(state);
+ }
+ return metric;
+}
} // anonymous namespace
TEST_F(ConfigUpdateTest, TestSimpleMatcherPreserve) {
@@ -1537,6 +1555,115 @@
EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
}
+TEST_F(ConfigUpdateTest, TestValueMetricPreserve) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+ State sliceState = CreateScreenState();
+ *config.add_state() = sliceState;
+
+ *config.add_value_metric() =
+ createValueMetric("VALUE1", whatMatcher, predicate.id(), {sliceState.id()});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricDefinitionChange) {
+ StatsdConfig config;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, nullopt, {});
+ EXPECT_TRUE(initConfig(config));
+
+ // Change skip zero diff output, which should change the proto, causing replacement.
+ config.mutable_value_metric(0)->set_skip_zero_diff_output(true);
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricWhatChanged) {
+ StatsdConfig config;
+ AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, nullopt, {});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricConditionChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, predicate.id(), {});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricStateChanged) {
+ StatsdConfig config;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ State sliceState = CreateScreenState();
+ *config.add_state() = sliceState;
+
+ *config.add_value_metric() =
+ createValueMetric("VALUE1", whatMatcher, nullopt, {sliceState.id()});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{sliceState.id()}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
TEST_F(ConfigUpdateTest, TestUpdateEventMetrics) {
StatsdConfig config;
@@ -1685,6 +1812,7 @@
unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
vector<int> metricsWithActivation;
+ set<int64_t> replacedMetrics;
EXPECT_TRUE(updateMetrics(
key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
@@ -1693,13 +1821,14 @@
/*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation));
+ metricsWithActivation, replacedMetrics));
unordered_map<int64_t, int> expectedMetricProducerMap = {
{event1Id, event1Index}, {event2Id, event2Index}, {event3Id, event3Index},
{event4Id, event4Index}, {event6Id, event6Index},
};
EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+ EXPECT_EQ(replacedMetrics, set<int64_t>({event2Id, event3Id, event4Id}));
// Make sure preserved metrics are the same.
ASSERT_EQ(newMetricProducers.size(), 5);
@@ -1914,6 +2043,7 @@
unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
vector<int> metricsWithActivation;
+ set<int64_t> replacedMetrics;
EXPECT_TRUE(updateMetrics(
key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
@@ -1922,13 +2052,14 @@
oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers,
conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation));
+ metricsWithActivation, replacedMetrics));
unordered_map<int64_t, int> expectedMetricProducerMap = {
{count1Id, count1Index}, {count2Id, count2Index}, {count3Id, count3Index},
{count4Id, count4Index}, {count6Id, count6Index},
};
EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+ EXPECT_EQ(replacedMetrics, set<int64_t>({count2Id, count3Id, count4Id}));
// Make sure preserved metrics are the same.
ASSERT_EQ(newMetricProducers.size(), 5);
@@ -2123,6 +2254,7 @@
unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
vector<int> metricsWithActivation;
+ set<int64_t> replacedMetrics;
EXPECT_TRUE(updateMetrics(
key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
@@ -2131,13 +2263,14 @@
/*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation));
+ metricsWithActivation, replacedMetrics));
unordered_map<int64_t, int> expectedMetricProducerMap = {
{gauge1Id, gauge1Index}, {gauge2Id, gauge2Index}, {gauge3Id, gauge3Index},
{gauge4Id, gauge4Index}, {gauge6Id, gauge6Index},
};
EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+ EXPECT_EQ(replacedMetrics, set<int64_t>({gauge2Id, gauge3Id, gauge4Id}));
// Make sure preserved metrics are the same.
ASSERT_EQ(newMetricProducers.size(), 5);
@@ -2440,6 +2573,7 @@
unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
vector<int> metricsWithActivation;
+ set<int64_t> replacedMetrics;
EXPECT_TRUE(updateMetrics(
key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, /*replacedMatchers=*/{},
@@ -2448,7 +2582,7 @@
oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers,
conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation));
+ metricsWithActivation, replacedMetrics));
unordered_map<int64_t, int> expectedMetricProducerMap = {
{duration1Id, duration1Index}, {duration2Id, duration2Index},
@@ -2456,7 +2590,7 @@
{duration6Id, duration6Index},
};
EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
-
+ EXPECT_EQ(replacedMetrics, set<int64_t>({duration2Id, duration3Id, duration4Id}));
// Make sure preserved metrics are the same.
ASSERT_EQ(newMetricProducers.size(), 5);
EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(duration1Id)],
@@ -2559,6 +2693,245 @@
EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
}
+TEST_F(ConfigUpdateTest, TestUpdateValueMetrics) {
+ StatsdConfig config;
+
+ // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig.
+ AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+ int64_t matcher1Id = matcher1.id();
+ *config.add_atom_matcher() = matcher1;
+
+ AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+ int64_t matcher2Id = matcher2.id();
+ *config.add_atom_matcher() = matcher2;
+
+ AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+ int64_t matcher3Id = matcher3.id();
+ *config.add_atom_matcher() = matcher3;
+
+ AtomMatcher matcher4 = CreateTemperatureAtomMatcher();
+ int64_t matcher4Id = matcher4.id();
+ *config.add_atom_matcher() = matcher4;
+
+ AtomMatcher matcher5 = CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE);
+ int64_t matcher5Id = matcher5.id();
+ *config.add_atom_matcher() = matcher5;
+
+ Predicate predicate1 = CreateScreenIsOnPredicate();
+ int64_t predicate1Id = predicate1.id();
+ *config.add_predicate() = predicate1;
+
+ Predicate predicate2 = CreateScreenIsOffPredicate();
+ int64_t predicate2Id = predicate2.id();
+ *config.add_predicate() = predicate2;
+
+ State state1 = CreateScreenStateWithOnOffMap(0x123, 0x321);
+ int64_t state1Id = state1.id();
+ *config.add_state() = state1;
+
+ State state2 = CreateScreenState();
+ int64_t state2Id = state2.id();
+ *config.add_state() = state2;
+
+ // Add a few value metrics.
+ // Note that these will not work as "real" metrics since the value field is always 2.
+ // Will be preserved.
+ ValueMetric value1 = createValueMetric("VALUE1", matcher4, predicate1Id, {state1Id});
+ int64_t value1Id = value1.id();
+ *config.add_value_metric() = value1;
+
+ // Will be replaced - definition change.
+ ValueMetric value2 = createValueMetric("VALUE2", matcher1, nullopt, {});
+ int64_t value2Id = value2.id();
+ *config.add_value_metric() = value2;
+
+ // Will be replaced - condition change.
+ ValueMetric value3 = createValueMetric("VALUE3", matcher5, predicate2Id, {});
+ int64_t value3Id = value3.id();
+ *config.add_value_metric() = value3;
+
+ // Will be replaced - state change.
+ ValueMetric value4 = createValueMetric("VALUE4", matcher3, nullopt, {state2Id});
+ int64_t value4Id = value4.id();
+ *config.add_value_metric() = value4;
+
+ // Will be deleted.
+ ValueMetric value5 = createValueMetric("VALUE5", matcher2, nullopt, {});
+ int64_t value5Id = value5.id();
+ *config.add_value_metric() = value5;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Used later to ensure the condition wizard is replaced. Get it before doing the update.
+ sp<EventMatcherWizard> oldMatcherWizard =
+ static_cast<ValueMetricProducer*>(oldMetricProducers[0].get())->mEventMatcherWizard;
+ EXPECT_EQ(oldMatcherWizard->getStrongCount(), 6);
+
+ // Change value2, causing it to be replaced.
+ value2.set_aggregation_type(ValueMetric::AVG);
+
+ // Mark predicate 2 as replaced. Causes value3 to be replaced.
+ set<int64_t> replacedConditions = {predicate2Id};
+
+ // Mark state 2 as replaced. Causes value4 to be replaced.
+ set<int64_t> replacedStates = {state2Id};
+
+ // New value metric.
+ ValueMetric value6 = createValueMetric("VALUE6", matcher5, predicate1Id, {state1Id});
+ int64_t value6Id = value6.id();
+
+ // Map the matchers and predicates in reverse order to force the indices to change.
+ std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ const int matcher5Index = 0;
+ newAtomMatchingTrackerMap[matcher5Id] = 0;
+ const int matcher4Index = 1;
+ newAtomMatchingTrackerMap[matcher4Id] = 1;
+ const int matcher3Index = 2;
+ newAtomMatchingTrackerMap[matcher3Id] = 2;
+ const int matcher2Index = 3;
+ newAtomMatchingTrackerMap[matcher2Id] = 3;
+ const int matcher1Index = 4;
+ newAtomMatchingTrackerMap[matcher1Id] = 4;
+ // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+ vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5);
+ std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+ newAtomMatchingTrackers.begin());
+
+ std::unordered_map<int64_t, int> newConditionTrackerMap;
+ const int predicate2Index = 0;
+ newConditionTrackerMap[predicate2Id] = 0;
+ const int predicate1Index = 1;
+ newConditionTrackerMap[predicate1Id] = 1;
+ // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
+ vector<sp<ConditionTracker>> newConditionTrackers(2);
+ std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+ newConditionTrackers.begin());
+ // Say that predicate1 & predicate2 is unknown since the initial condition never changed.
+ vector<ConditionState> conditionCache = {ConditionState::kUnknown, ConditionState::kUnknown};
+
+ StatsdConfig newConfig;
+ *newConfig.add_value_metric() = value6;
+ const int value6Index = 0;
+ *newConfig.add_value_metric() = value3;
+ const int value3Index = 1;
+ *newConfig.add_value_metric() = value1;
+ const int value1Index = 2;
+ *newConfig.add_value_metric() = value4;
+ const int value4Index = 3;
+ *newConfig.add_value_metric() = value2;
+ const int value2Index = 4;
+
+ *newConfig.add_state() = state1;
+ *newConfig.add_state() = state2;
+
+ unordered_map<int64_t, int> stateAtomIdMap;
+ unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
+ map<int64_t, uint64_t> stateProtoHashes;
+ EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes));
+
+ // Output data structures to validate.
+ unordered_map<int64_t, int> newMetricProducerMap;
+ vector<sp<MetricProducer>> newMetricProducers;
+ unordered_map<int, vector<int>> conditionToMetricMap;
+ unordered_map<int, vector<int>> trackerToMetricMap;
+ set<int64_t> noReportMetricIds;
+ unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+ vector<int> metricsWithActivation;
+ set<int64_t> replacedMetrics;
+ EXPECT_TRUE(updateMetrics(
+ key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, /*replacedMatchers=*/{},
+ newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
+ newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates,
+ oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers,
+ conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, replacedMetrics));
+
+ unordered_map<int64_t, int> expectedMetricProducerMap = {
+ {value1Id, value1Index}, {value2Id, value2Index}, {value3Id, value3Index},
+ {value4Id, value4Index}, {value6Id, value6Index},
+ };
+ EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+ EXPECT_EQ(replacedMetrics, set<int64_t>({value2Id, value3Id, value4Id}));
+
+ // Make sure preserved metrics are the same.
+ ASSERT_EQ(newMetricProducers.size(), 5);
+ EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(value1Id)],
+ newMetricProducers[newMetricProducerMap.at(value1Id)]);
+
+ // Make sure replaced metrics are different.
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(value2Id)],
+ newMetricProducers[newMetricProducerMap.at(value2Id)]);
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(value3Id)],
+ newMetricProducers[newMetricProducerMap.at(value3Id)]);
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(value4Id)],
+ newMetricProducers[newMetricProducerMap.at(value4Id)]);
+
+ // Verify the conditionToMetricMap.
+ ASSERT_EQ(conditionToMetricMap.size(), 2);
+ const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
+ EXPECT_THAT(condition1Metrics, UnorderedElementsAre(value1Index, value6Index));
+ const vector<int>& condition2Metrics = conditionToMetricMap[predicate2Index];
+ EXPECT_THAT(condition2Metrics, UnorderedElementsAre(value3Index));
+
+ // Verify the trackerToMetricMap.
+ ASSERT_EQ(trackerToMetricMap.size(), 4);
+ const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+ EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(value2Index));
+ const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+ EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(value4Index));
+ const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
+ EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(value1Index));
+ const vector<int>& matcher5Metrics = trackerToMetricMap[matcher5Index];
+ EXPECT_THAT(matcher5Metrics, UnorderedElementsAre(value3Index, value6Index));
+
+ // Verify event activation/deactivation maps.
+ ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
+ ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+ ASSERT_EQ(metricsWithActivation.size(), 0);
+
+ // Verify tracker indices/ids/conditions/states are correct.
+ ValueMetricProducer* valueProducer1 =
+ static_cast<ValueMetricProducer*>(newMetricProducers[value1Index].get());
+ EXPECT_EQ(valueProducer1->getMetricId(), value1Id);
+ EXPECT_EQ(valueProducer1->mConditionTrackerIndex, predicate1Index);
+ EXPECT_EQ(valueProducer1->mCondition, ConditionState::kUnknown);
+ EXPECT_EQ(valueProducer1->mWhatMatcherIndex, matcher4Index);
+ ValueMetricProducer* valueProducer2 =
+ static_cast<ValueMetricProducer*>(newMetricProducers[value2Index].get());
+ EXPECT_EQ(valueProducer2->getMetricId(), value2Id);
+ EXPECT_EQ(valueProducer2->mConditionTrackerIndex, -1);
+ EXPECT_EQ(valueProducer2->mCondition, ConditionState::kTrue);
+ EXPECT_EQ(valueProducer2->mWhatMatcherIndex, matcher1Index);
+ ValueMetricProducer* valueProducer3 =
+ static_cast<ValueMetricProducer*>(newMetricProducers[value3Index].get());
+ EXPECT_EQ(valueProducer3->getMetricId(), value3Id);
+ EXPECT_EQ(valueProducer3->mConditionTrackerIndex, predicate2Index);
+ EXPECT_EQ(valueProducer3->mCondition, ConditionState::kUnknown);
+ EXPECT_EQ(valueProducer3->mWhatMatcherIndex, matcher5Index);
+ ValueMetricProducer* valueProducer4 =
+ static_cast<ValueMetricProducer*>(newMetricProducers[value4Index].get());
+ EXPECT_EQ(valueProducer4->getMetricId(), value4Id);
+ EXPECT_EQ(valueProducer4->mConditionTrackerIndex, -1);
+ EXPECT_EQ(valueProducer4->mCondition, ConditionState::kTrue);
+ EXPECT_EQ(valueProducer4->mWhatMatcherIndex, matcher3Index);
+ ValueMetricProducer* valueProducer6 =
+ static_cast<ValueMetricProducer*>(newMetricProducers[value6Index].get());
+ EXPECT_EQ(valueProducer6->getMetricId(), value6Id);
+ EXPECT_EQ(valueProducer6->mConditionTrackerIndex, predicate1Index);
+ EXPECT_EQ(valueProducer6->mCondition, ConditionState::kUnknown);
+ EXPECT_EQ(valueProducer6->mWhatMatcherIndex, matcher5Index);
+
+ sp<EventMatcherWizard> newMatcherWizard = valueProducer1->mEventMatcherWizard;
+ EXPECT_NE(newMatcherWizard, oldMatcherWizard);
+ EXPECT_EQ(newMatcherWizard->getStrongCount(), 6);
+ oldMetricProducers.clear();
+ // Only reference to the old wizard should be the one in the test.
+ EXPECT_EQ(oldMatcherWizard->getStrongCount(), 1);
+}
+
TEST_F(ConfigUpdateTest, TestUpdateMetricActivations) {
StatsdConfig config;
// Add atom matchers
@@ -2641,6 +3014,7 @@
unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
vector<int> metricsWithActivation;
+ set<int64_t> replacedMetrics;
EXPECT_TRUE(updateMetrics(
key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
@@ -2649,7 +3023,7 @@
/*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation));
+ metricsWithActivation, replacedMetrics));
// Verify event activation/deactivation maps.
ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 3);
@@ -2724,6 +3098,11 @@
*config.add_gauge_metric() = gaugeMetric;
// Preserved.
+ ValueMetric valueMetric = createValueMetric("VALUE1", matcher3, predicate1Id, {});
+ int64_t valueMetricId = valueMetric.id();
+ *config.add_value_metric() = valueMetric;
+
+ // Preserved.
DurationMetric durationMetric = createDurationMetric("DURATION1", predicate1Id, nullopt, {});
int64_t durationMetricId = durationMetric.id();
*config.add_duration_metric() = durationMetric;
@@ -2732,7 +3111,7 @@
// Used later to ensure the condition wizard is replaced. Get it before doing the update.
sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard;
- EXPECT_EQ(oldConditionWizard->getStrongCount(), 5);
+ EXPECT_EQ(oldConditionWizard->getStrongCount(), 6);
// Mark matcher 2 as replaced. Causes eventMetric to be replaced.
set<int64_t> replacedMatchers;
@@ -2763,6 +3142,7 @@
newConditionTrackers.begin());
vector<ConditionState> conditionCache = {ConditionState::kUnknown};
+ // The order matters. we parse in the order of: count, duration, event, value, gauge.
StatsdConfig newConfig;
*newConfig.add_count_metric() = countMetric;
const int countMetricIndex = 0;
@@ -2770,8 +3150,10 @@
const int durationMetricIndex = 1;
*newConfig.add_event_metric() = eventMetric;
const int eventMetricIndex = 2;
+ *newConfig.add_value_metric() = valueMetric;
+ const int valueMetricIndex = 3;
*newConfig.add_gauge_metric() = gaugeMetric;
- const int gaugeMetricIndex = 3;
+ const int gaugeMetricIndex = 4;
// Add the predicate since duration metric needs it.
*newConfig.add_predicate() = predicate1;
@@ -2785,6 +3167,7 @@
unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
vector<int> metricsWithActivation;
+ set<int64_t> replacedMetrics;
EXPECT_TRUE(updateMetrics(
key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
@@ -2793,22 +3176,25 @@
/*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation));
+ metricsWithActivation, replacedMetrics));
unordered_map<int64_t, int> expectedMetricProducerMap = {
- {countMetricId, countMetricIndex},
- {durationMetricId, durationMetricIndex},
- {eventMetricId, eventMetricIndex},
+ {countMetricId, countMetricIndex}, {durationMetricId, durationMetricIndex},
+ {eventMetricId, eventMetricIndex}, {valueMetricId, valueMetricIndex},
{gaugeMetricId, gaugeMetricIndex},
};
EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+ EXPECT_EQ(replacedMetrics, set<int64_t>({eventMetricId, gaugeMetricId}));
+
// Make sure preserved metrics are the same.
- ASSERT_EQ(newMetricProducers.size(), 4);
+ ASSERT_EQ(newMetricProducers.size(), 5);
EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(countMetricId)],
newMetricProducers[newMetricProducerMap.at(countMetricId)]);
EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(durationMetricId)],
newMetricProducers[newMetricProducerMap.at(durationMetricId)]);
+ EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(valueMetricId)],
+ newMetricProducers[newMetricProducerMap.at(valueMetricId)]);
// Make sure replaced metrics are different.
EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(eventMetricId)],
@@ -2819,7 +3205,8 @@
// Verify the conditionToMetricMap.
ASSERT_EQ(conditionToMetricMap.size(), 1);
const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
- EXPECT_THAT(condition1Metrics, UnorderedElementsAre(countMetricIndex, gaugeMetricIndex));
+ EXPECT_THAT(condition1Metrics,
+ UnorderedElementsAre(countMetricIndex, gaugeMetricIndex, valueMetricIndex));
// Verify the trackerToMetricMap.
ASSERT_EQ(trackerToMetricMap.size(), 3);
@@ -2828,7 +3215,7 @@
const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(eventMetricIndex, durationMetricIndex));
const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
- EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(gaugeMetricIndex));
+ EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(gaugeMetricIndex, valueMetricIndex));
// Verify event activation/deactivation maps.
ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
@@ -2851,7 +3238,7 @@
sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard;
EXPECT_NE(newConditionWizard, oldConditionWizard);
- EXPECT_EQ(newConditionWizard->getStrongCount(), 5);
+ EXPECT_EQ(newConditionWizard->getStrongCount(), 6);
oldMetricProducers.clear();
// Only reference to the old wizard should be the one in the test.
EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 250f2f0..74da95f 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4812,13 +4812,14 @@
/**
* Holds the AM lock for the specified amount of milliseconds.
* This is intended for use by the tests that need to imitate lock contention.
+ * The token should be obtained by
+ * {@link android.content.pm.PackageManager#getHoldLockToken()}.
* @hide
*/
@TestApi
- @RequiresPermission(android.Manifest.permission.INJECT_EVENTS)
- public void holdLock(int durationMs) {
+ public void holdLock(IBinder token, int durationMs) {
try {
- getService().holdLock(durationMs);
+ getService().holdLock(token, durationMs);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 89cb8b8..4bc8a5e 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -475,4 +475,10 @@
* @return true if exists, false otherwise.
*/
public abstract boolean isPendingTopUid(int uid);
+
+ /**
+ * @return the intent for the given intent sender.
+ */
+ @Nullable
+ public abstract Intent getIntentForIntentSender(IIntentSender sender);
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4ea2aac..87c729b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -513,7 +513,6 @@
@UnsupportedAppUsage
boolean stopped;
boolean hideForNow;
- Configuration newConfig;
Configuration createdConfig;
Configuration overrideConfig;
// Used to save the last reported configuration from server side so that activity
@@ -2222,21 +2221,6 @@
return sPermissionManager;
}
- private Configuration mMainThreadConfig = new Configuration();
-
- Configuration applyConfigCompatMainThread(int displayDensity, Configuration config,
- CompatibilityInfo compat) {
- if (config == null) {
- return null;
- }
- if (!compat.supportsScreen()) {
- mMainThreadConfig.setTo(config);
- config = mMainThreadConfig;
- compat.applyToConfiguration(displayDensity, config);
- }
- return config;
- }
-
/**
* Creates the top level resources for the given package. Will return an existing
* Resources if one has already been created.
@@ -4597,14 +4581,6 @@
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
- if (r.newConfig != null) {
- performConfigurationChangedForActivity(r, r.newConfig);
- if (DEBUG_CONFIGURATION) {
- Slog.v(TAG, "Resuming activity " + r.activityInfo.name + " with newConfig "
- + r.activity.mCurrentConfig);
- }
- r.newConfig = null;
- }
if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward);
ViewRootImpl impl = r.window.getDecorView().getViewRootImpl();
WindowManager.LayoutParams l = impl != null
@@ -4910,13 +4886,6 @@
r.activity.makeVisible();
}
}
- if (r.newConfig != null) {
- performConfigurationChangedForActivity(r, r.newConfig);
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Updating activity vis "
- + r.activityInfo.name + " with new config "
- + r.activity.mCurrentConfig);
- r.newConfig = null;
- }
} else {
if (r.activity.mVisibleFromServer) {
r.activity.mVisibleFromServer = false;
@@ -5488,8 +5457,7 @@
}
}
- ArrayList<ComponentCallbacks2> collectComponentCallbacks(
- boolean allActivities, Configuration newConfig) {
+ ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeActivities) {
ArrayList<ComponentCallbacks2> callbacks
= new ArrayList<ComponentCallbacks2>();
@@ -5498,29 +5466,11 @@
for (int i=0; i<NAPP; i++) {
callbacks.add(mAllApplications.get(i));
}
- final int NACT = mActivities.size();
- for (int i=0; i<NACT; i++) {
- ActivityClientRecord ar = mActivities.valueAt(i);
- Activity a = ar.activity;
- if (a != null) {
- Configuration thisConfig = applyConfigCompatMainThread(
- mCurDefaultDisplayDpi, newConfig,
- ar.packageInfo.getCompatibilityInfo());
- if (!ar.activity.mFinished && (allActivities || !ar.paused)) {
- // If the activity is currently resumed, its configuration
- // needs to change right now.
+ if (includeActivities) {
+ for (int i = mActivities.size() - 1; i >= 0; i--) {
+ final Activity a = mActivities.valueAt(i).activity;
+ if (a != null && !a.mFinished) {
callbacks.add(a);
- } else if (thisConfig != null) {
- // Otherwise, we will tell it about the change
- // the next time it is resumed or shown. Note that
- // the activity manager may, before then, decide the
- // activity needs to be destroyed to handle its new
- // configuration.
- if (DEBUG_CONFIGURATION) {
- Slog.v(TAG, "Setting activity "
- + ar.activityInfo.name + " newConfig=" + thisConfig);
- }
- ar.newConfig = thisConfig;
}
}
}
@@ -5544,17 +5494,6 @@
}
/**
- * Updates the configuration for an Activity in its current display.
- *
- * @see #performConfigurationChangedForActivity(ActivityClientRecord, Configuration, int,
- * boolean)
- */
- private void performConfigurationChangedForActivity(ActivityClientRecord r,
- Configuration newBaseConfig) {
- performConfigurationChangedForActivity(r, newBaseConfig, r.activity.getDisplayId());
- }
-
- /**
* Updates the configuration for an Activity. The ActivityClientRecord's
* {@link ActivityClientRecord#overrideConfig} is used to compute the final Configuration for
* that Activity. {@link ActivityClientRecord#tmpConfig} is used as a temporary for delivering
@@ -5804,7 +5743,8 @@
}
}
- ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config);
+ final ArrayList<ComponentCallbacks2> callbacks =
+ collectComponentCallbacks(false /* includeActivities */);
freeTextLayoutCachesIfNeeded(configDiff);
@@ -5812,13 +5752,7 @@
final int N = callbacks.size();
for (int i=0; i<N; i++) {
ComponentCallbacks2 cb = callbacks.get(i);
- if (cb instanceof Activity) {
- // If callback is an Activity - call corresponding method to consider override
- // config and avoid onConfigurationChanged if it hasn't changed.
- Activity a = (Activity) cb;
- performConfigurationChangedForActivity(mActivities.get(a.getActivityToken()),
- config);
- } else if (!equivalent) {
+ if (!equivalent) {
performConfigurationChanged(cb, config);
} else {
// TODO (b/135719017): Temporary log for debugging IME service.
@@ -6206,7 +6140,8 @@
}
final void handleLowMemory() {
- ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(true, null);
+ final ArrayList<ComponentCallbacks2> callbacks =
+ collectComponentCallbacks(true /* includeActivities */);
final int N = callbacks.size();
for (int i=0; i<N; i++) {
@@ -6238,7 +6173,8 @@
}
}
- ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(true, null);
+ final ArrayList<ComponentCallbacks2> callbacks =
+ collectComponentCallbacks(true /* includeActivities */);
final int N = callbacks.size();
for (int i = 0; i < N; i++) {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index f3b3789..3bcb87a 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -865,6 +865,8 @@
@UnsupportedAppUsage
public static final int OP_SEND_SMS = AppProtoEnums.APP_OP_SEND_SMS;
/** @hide */
+ public static final int OP_MANAGE_ONGOING_CALLS = AppProtoEnums.APP_OP_MANAGE_ONGOING_CALLS;
+ /** @hide */
@UnsupportedAppUsage
public static final int OP_READ_ICC_SMS = AppProtoEnums.APP_OP_READ_ICC_SMS;
/** @hide */
@@ -1155,7 +1157,7 @@
/** @hide */
@UnsupportedAppUsage
- public static final int _NUM_OP = 103;
+ public static final int _NUM_OP = 104;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1466,6 +1468,15 @@
public static final String OPSTR_LOADER_USAGE_STATS = "android:loader_usage_stats";
/**
+ * Grants an app access to the {@link android.telecom.InCallService} API to see
+ * information about ongoing calls and to enable control of calls.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final String OPSTR_MANAGE_ONGOING_CALLS = "android:manage_ongoing_calls";
+
+ /**
* AppOp granted to apps that we are started via {@code am instrument -e --no-isolated-storage}
*
* <p>MediaProvider is the only component (outside of system server) that should care about this
@@ -1574,6 +1585,7 @@
OP_MANAGE_EXTERNAL_STORAGE,
OP_INTERACT_ACROSS_PROFILES,
OP_LOADER_USAGE_STATS,
+ OP_MANAGE_ONGOING_CALLS,
};
/**
@@ -1688,6 +1700,7 @@
OP_PHONE_CALL_MICROPHONE, // OP_PHONE_CALL_MICROPHONE
OP_PHONE_CALL_CAMERA, // OP_PHONE_CALL_CAMERA
OP_RECORD_AUDIO_HOTWORD, // RECORD_AUDIO_HOTWORD
+ OP_MANAGE_ONGOING_CALLS, // MANAGE_ONGOING_CALLS
};
/**
@@ -1797,6 +1810,7 @@
OPSTR_PHONE_CALL_MICROPHONE,
OPSTR_PHONE_CALL_CAMERA,
OPSTR_RECORD_AUDIO_HOTWORD,
+ OPSTR_MANAGE_ONGOING_CALLS,
};
/**
@@ -1907,6 +1921,7 @@
"PHONE_CALL_MICROPHONE",
"PHONE_CALL_CAMERA",
"RECORD_AUDIO_HOTWORD",
+ "MANAGE_ONGOING_CALLS",
};
/**
@@ -2018,6 +2033,7 @@
null, // no permission for OP_PHONE_CALL_MICROPHONE
null, // no permission for OP_PHONE_CALL_CAMERA
null, // no permission for OP_RECORD_AUDIO_HOTWORD
+ Manifest.permission.MANAGE_ONGOING_CALLS,
};
/**
@@ -2129,6 +2145,7 @@
null, // PHONE_CALL_MICROPHONE
null, // PHONE_CALL_MICROPHONE
null, // RECORD_AUDIO_HOTWORD
+ null, // MANAGE_ONGOING_CALLS
};
/**
@@ -2239,6 +2256,7 @@
null, // PHONE_CALL_MICROPHONE
null, // PHONE_CALL_CAMERA
null, // RECORD_AUDIO_HOTWORD
+ null, // MANAGE_ONGOING_CALLS
};
/**
@@ -2348,6 +2366,7 @@
AppOpsManager.MODE_ALLOWED, // PHONE_CALL_MICROPHONE
AppOpsManager.MODE_ALLOWED, // PHONE_CALL_CAMERA
AppOpsManager.MODE_ALLOWED, // OP_RECORD_AUDIO_HOTWORD
+ AppOpsManager.MODE_DEFAULT, // MANAGE_ONGOING_CALLS
};
/**
@@ -2461,6 +2480,7 @@
false, // PHONE_CALL_MICROPHONE
false, // PHONE_CALL_CAMERA
false, // RECORD_AUDIO_HOTWORD
+ true, // MANAGE_ONGOING_CALLS
};
/**
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 357b26c..c0e3019 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -689,6 +689,8 @@
/**
* Holds the AM lock for the specified amount of milliseconds.
* This is intended for use by the tests that need to imitate lock contention.
+ * The token should be obtained by
+ * {@link android.content.pm.PackageManager#getHoldLockToken()}.
*/
- void holdLock(in int durationMs);
+ void holdLock(in IBinder token, in int durationMs);
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index a970322..66a7f4d 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -98,7 +98,7 @@
void updateNotificationChannelForPackage(String pkg, int uid, in NotificationChannel channel);
NotificationChannel getNotificationChannel(String callingPkg, int userId, String pkg, String channelId);
NotificationChannel getConversationNotificationChannel(String callingPkg, int userId, String pkg, String channelId, boolean returnParentIfNoConversationChannel, String conversationId);
- void createConversationNotificationChannelForPackage(String pkg, int uid, String triggeringKey, in NotificationChannel parentChannel, String conversationId);
+ void createConversationNotificationChannelForPackage(String pkg, int uid, in NotificationChannel parentChannel, String conversationId);
NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, String conversationId, boolean includeDeleted);
void deleteNotificationChannel(String pkg, String channelId);
void deleteConversationNotificationChannels(String pkg, int uid, String conversationId);
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index acc42dbc..37c4c92 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -130,6 +130,7 @@
FLAG_UPDATE_CURRENT,
FLAG_IMMUTABLE,
FLAG_MUTABLE,
+ FLAG_MUTABLE_UNAUDITED,
Intent.FILL_IN_ACTION,
Intent.FILL_IN_DATA,
@@ -205,6 +206,13 @@
public static final int FLAG_MUTABLE = 1<<25;
/**
+ * @deprecated Use {@link #FLAG_IMMUTABLE} or {@link #FLAG_MUTABLE} instead.
+ * @hide
+ */
+ @Deprecated
+ public static final int FLAG_MUTABLE_UNAUDITED = FLAG_MUTABLE;
+
+ /**
* Exception thrown when trying to send through a PendingIntent that
* has been canceled or is otherwise no longer able to execute the request.
*/
@@ -397,25 +405,13 @@
* parameters. May return null only if {@link #FLAG_NO_CREATE} has been
* supplied.
*/
+ @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
public static PendingIntent getActivity(Context context, int requestCode,
@NonNull Intent intent, @Flags int flags, @Nullable Bundle options) {
- String packageName = context.getPackageName();
- String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
- context.getContentResolver()) : null;
- checkFlags(flags, packageName);
- try {
- intent.migrateExtraStreamToClipData(context);
- intent.prepareToLeaveProcess(context);
- IIntentSender target =
- ActivityManager.getService().getIntentSenderWithFeature(
- ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
- context.getAttributionTag(), null, null, requestCode, new Intent[] { intent },
- resolvedType != null ? new String[] { resolvedType } : null,
- flags, options, context.getUserId());
- return target != null ? new PendingIntent(target) : null;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ // Some tests only mock Context.getUserId(), so fallback to the id Context.getUser() is null
+ final UserHandle user = context.getUser();
+ return getActivityAsUser(context, requestCode, intent, flags, options,
+ user != null ? user : UserHandle.of(context.getUserId()));
}
/**
@@ -541,26 +537,13 @@
* parameters. May return null only if {@link #FLAG_NO_CREATE} has been
* supplied.
*/
+ @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
public static PendingIntent getActivities(Context context, int requestCode,
@NonNull Intent[] intents, @Flags int flags, @Nullable Bundle options) {
- String packageName = context.getPackageName();
- String[] resolvedTypes = new String[intents.length];
- for (int i=0; i<intents.length; i++) {
- intents[i].migrateExtraStreamToClipData(context);
- intents[i].prepareToLeaveProcess(context);
- resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
- }
- checkFlags(flags, packageName);
- try {
- IIntentSender target =
- ActivityManager.getService().getIntentSenderWithFeature(
- ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
- context.getAttributionTag(), null, null, requestCode, intents, resolvedTypes,
- flags, options, context.getUserId());
- return target != null ? new PendingIntent(target) : null;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ // Some tests only mock Context.getUserId(), so fallback to the id Context.getUser() is null
+ final UserHandle user = context.getUser();
+ return getActivitiesAsUser(context, requestCode, intents, flags, options,
+ user != null ? user : UserHandle.of(context.getUserId()));
}
/**
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 849f679..5caf305 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -24,6 +24,8 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
@@ -181,6 +183,20 @@
public boolean isResizeable;
/**
+ * Activity bounds if this task or its top activity is presented in letterbox mode and
+ * {@code null} otherwise.
+ * @hide
+ */
+ @Nullable
+ public Rect letterboxActivityBounds;
+
+ /**
+ * Relative position of the task's top left corner in the parent container.
+ * @hide
+ */
+ public Point positionInParent;
+
+ /**
* The launch cookies associated with activities in this task if any.
* @see ActivityOptions#setLaunchCookie(IBinder)
* @hide
@@ -225,6 +241,7 @@
/** @hide */
public void addLaunchCookie(IBinder cookie) {
+ if (cookie == null || launchCookies.contains(cookie)) return;
launchCookies.add(cookie);
}
@@ -256,6 +273,8 @@
topActivityInfo = source.readTypedObject(ActivityInfo.CREATOR);
isResizeable = source.readBoolean();
source.readBinderList(launchCookies);
+ letterboxActivityBounds = source.readTypedObject(Rect.CREATOR);
+ positionInParent = source.readTypedObject(Point.CREATOR);
}
/**
@@ -287,6 +306,8 @@
dest.writeTypedObject(topActivityInfo, flags);
dest.writeBoolean(isResizeable);
dest.writeBinderList(launchCookies);
+ dest.writeTypedObject(letterboxActivityBounds, flags);
+ dest.writeTypedObject(positionInParent, flags);
}
@Override
@@ -306,6 +327,9 @@
+ " topActivityType=" + topActivityType
+ " pictureInPictureParams=" + pictureInPictureParams
+ " topActivityInfo=" + topActivityInfo
- + " launchCookies" + launchCookies;
+ + " launchCookies" + launchCookies
+ + " letterboxActivityBounds=" + letterboxActivityBounds
+ + " positionInParent=" + positionInParent
+ + "}";
}
}
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 4ae1670..c4af4ed 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -796,6 +796,9 @@
/**
* Returns {@code true} if the windowingMode represents a window in multi-window mode.
* I.e. sharing the screen with another activity.
+ *
+ * TODO(b/171672645): This API could be misleading - in 'undefined' mode it's determined by the
+ * parent's mode
* @hide
*/
public static boolean inMultiWindowMode(int windowingMode) {
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index 3cc7f1e..4c541b3 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -55,22 +55,6 @@
static final String TAG = "DeviceAdminInfo";
/**
- * A type of policy that this device admin can use: device owner meta-policy
- * for an admin that is designated as owner of the device.
- *
- * @hide
- */
- public static final int USES_POLICY_DEVICE_OWNER = -2;
-
- /**
- * A type of policy that this device admin can use: profile owner meta-policy
- * for admins that have been installed as owner of some user profile.
- *
- * @hide
- */
- public static final int USES_POLICY_PROFILE_OWNER = -1;
-
- /**
* A type of policy that this device admin can use: limit the passwords
* that the user can select, via {@link DevicePolicyManager#setPasswordQuality}
* and {@link DevicePolicyManager#setPasswordMinimumLength}.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 054e842..8d4de26 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -525,7 +525,7 @@
* @hide
*/
public static final String ACTION_REMOTE_BUGREPORT_DISPATCH =
- "android.intent.action.REMOTE_BUGREPORT_DISPATCH";
+ "com.android.server.action.REMOTE_BUGREPORT_DISPATCH";
/**
* Extra for shared bugreport's SHA-256 hash.
@@ -1830,15 +1830,6 @@
public static final int STATE_USER_PROFILE_COMPLETE = 4;
/**
- * Management setup on a managed profile.
- * <p>This is used as an intermediate state after {@link #STATE_USER_PROFILE_COMPLETE} once the
- * work profile has been created.
- * @hide
- */
- @SystemApi
- public static final int STATE_USER_PROFILE_FINALIZED = 5;
-
- /**
* @hide
*/
@IntDef(prefix = { "STATE_USER_" }, value = {
@@ -1846,8 +1837,7 @@
STATE_USER_SETUP_INCOMPLETE,
STATE_USER_SETUP_COMPLETE,
STATE_USER_SETUP_FINALIZED,
- STATE_USER_PROFILE_COMPLETE,
- STATE_USER_PROFILE_FINALIZED
+ STATE_USER_PROFILE_COMPLETE
})
@Retention(RetentionPolicy.SOURCE)
public @interface UserProvisioningState {}
diff --git a/core/java/android/app/people/ConversationChannel.java b/core/java/android/app/people/ConversationChannel.java
index 39c5c85..a648b3b 100644
--- a/core/java/android/app/people/ConversationChannel.java
+++ b/core/java/android/app/people/ConversationChannel.java
@@ -17,6 +17,7 @@
package android.app.people;
import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
import android.content.pm.ShortcutInfo;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,7 +31,9 @@
public final class ConversationChannel implements Parcelable {
private ShortcutInfo mShortcutInfo;
+ private int mUid;
private NotificationChannel mParentNotificationChannel;
+ private NotificationChannelGroup mParentNotificationChannelGroup;
private long mLastEventTimestamp;
private boolean mHasActiveNotifications;
@@ -46,18 +49,24 @@
}
};
- public ConversationChannel(ShortcutInfo shortcutInfo,
- NotificationChannel parentNotificationChannel, long lastEventTimestamp,
+ public ConversationChannel(ShortcutInfo shortcutInfo, int uid,
+ NotificationChannel parentNotificationChannel,
+ NotificationChannelGroup parentNotificationChannelGroup, long lastEventTimestamp,
boolean hasActiveNotifications) {
mShortcutInfo = shortcutInfo;
+ mUid = uid;
mParentNotificationChannel = parentNotificationChannel;
+ mParentNotificationChannelGroup = parentNotificationChannelGroup;
mLastEventTimestamp = lastEventTimestamp;
mHasActiveNotifications = hasActiveNotifications;
}
public ConversationChannel(Parcel in) {
mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader());
+ mUid = in.readInt();
mParentNotificationChannel = in.readParcelable(NotificationChannel.class.getClassLoader());
+ mParentNotificationChannelGroup =
+ in.readParcelable(NotificationChannelGroup.class.getClassLoader());
mLastEventTimestamp = in.readLong();
mHasActiveNotifications = in.readBoolean();
}
@@ -70,7 +79,9 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(mShortcutInfo, flags);
+ dest.writeInt(mUid);
dest.writeParcelable(mParentNotificationChannel, flags);
+ dest.writeParcelable(mParentNotificationChannelGroup, flags);
dest.writeLong(mLastEventTimestamp);
dest.writeBoolean(mHasActiveNotifications);
}
@@ -79,10 +90,18 @@
return mShortcutInfo;
}
+ public int getUid() {
+ return mUid;
+ }
+
public NotificationChannel getParentNotificationChannel() {
return mParentNotificationChannel;
}
+ public NotificationChannelGroup getParentNotificationChannelGroup() {
+ return mParentNotificationChannelGroup;
+ }
+
public long getLastEventTimestamp() {
return mLastEventTimestamp;
}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 90890d5..fbb37db 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -241,6 +241,8 @@
int result = 17;
result = 31 * result + Objects.hashCode(mActivityCallbacks);
result = 31 * result + Objects.hashCode(mLifecycleStateRequest);
+ result = 31 * result + Objects.hashCode(mClient);
+ result = 31 * result + Objects.hashCode(mActivityToken);
return result;
}
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 6ce05f9..e6d6e7a 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -79,7 +79,7 @@
/**
* Intent used to broadcast the change in the Audio Connection state of the
- * A2DP profile.
+ * HDP profile.
*
* <p>This intent will have 3 extras:
* <ul>
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index 2342165..78eb859 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -22,6 +22,7 @@
import static android.content.ContentResolver.SCHEME_FILE;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.ActivityInfo;
import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.net.Uri;
@@ -201,6 +202,8 @@
final Intent mIntent;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Uri mUri;
+ // Additional activity info resolved by the system
+ ActivityInfo mActivityInfo;
/** @hide */
public Item(Item other) {
@@ -313,6 +316,22 @@
}
/**
+ * Retrieve the activity info contained in this Item.
+ * @hide
+ */
+ public ActivityInfo getActivityInfo() {
+ return mActivityInfo;
+ }
+
+ /**
+ * Updates the activity info for in this Item.
+ * @hide
+ */
+ public void setActivityInfo(ActivityInfo info) {
+ mActivityInfo = info;
+ }
+
+ /**
* Turn this item into text, regardless of the type of data it
* actually contains.
*
@@ -1129,18 +1148,9 @@
Item item = mItems.get(i);
TextUtils.writeToParcel(item.mText, dest, flags);
dest.writeString8(item.mHtmlText);
- if (item.mIntent != null) {
- dest.writeInt(1);
- item.mIntent.writeToParcel(dest, flags);
- } else {
- dest.writeInt(0);
- }
- if (item.mUri != null) {
- dest.writeInt(1);
- item.mUri.writeToParcel(dest, flags);
- } else {
- dest.writeInt(0);
- }
+ dest.writeTypedObject(item.mIntent, flags);
+ dest.writeTypedObject(item.mUri, flags);
+ dest.writeTypedObject(item.mActivityInfo, flags);
}
}
@@ -1151,14 +1161,17 @@
} else {
mIcon = null;
}
- mItems = new ArrayList<Item>();
+ mItems = new ArrayList<>();
final int N = in.readInt();
for (int i=0; i<N; i++) {
CharSequence text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
String htmlText = in.readString8();
- Intent intent = in.readInt() != 0 ? Intent.CREATOR.createFromParcel(in) : null;
- Uri uri = in.readInt() != 0 ? Uri.CREATOR.createFromParcel(in) : null;
- mItems.add(new Item(text, htmlText, intent, uri));
+ Intent intent = in.readTypedObject(Intent.CREATOR);
+ Uri uri = in.readTypedObject(Uri.CREATOR);
+ ActivityInfo info = in.readTypedObject(ActivityInfo.CREATOR);
+ Item item = new Item(text, htmlText, intent, uri);
+ item.setActivityInfo(info);
+ mItems.add(item);
}
}
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index 6739138..f9e63085 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -16,6 +16,7 @@
package android.content;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
@@ -64,6 +65,29 @@
public static final String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
/**
+ * The MIME type for an activity. The ClipData must include intents with required extras
+ * {@link #EXTRA_PENDING_INTENT} and {@link Intent#EXTRA_USER}, and an optional
+ * {@link #EXTRA_ACTIVITY_OPTIONS}.
+ * @hide
+ */
+ public static final String MIMETYPE_APPLICATION_ACTIVITY = "application/vnd.android.activity";
+
+ /**
+ * The MIME type for a shortcut. The ClipData must include intents with required extras
+ * {@link #EXTRA_PENDING_INTENT} and {@link Intent#EXTRA_USER}, and an optional
+ * {@link #EXTRA_ACTIVITY_OPTIONS}.
+ * @hide
+ */
+ public static final String MIMETYPE_APPLICATION_SHORTCUT = "application/vnd.android.shortcut";
+
+ /**
+ * The MIME type for a task. The ClipData must include an intent with a required extra
+ * {@link Intent#EXTRA_TASK_ID} of the task to launch.
+ * @hide
+ */
+ public static final String MIMETYPE_APPLICATION_TASK = "application/vnd.android.task";
+
+ /**
* The MIME type for data whose type is otherwise unknown.
* <p>
* Per RFC 2046, the "application" media type is to be used for discrete
@@ -74,31 +98,22 @@
public static final String MIMETYPE_UNKNOWN = "application/octet-stream";
/**
- * The name of the extra used to define a component name when copying/dragging
- * an app icon from Launcher.
+ * The pending intent for the activity to launch.
* <p>
- * Type: String
- * </p>
- * <p>
- * Use {@link ComponentName#unflattenFromString(String)}
- * and {@link ComponentName#flattenToString()} to convert the extra value
- * to/from {@link ComponentName}.
+ * Type: PendingIntent
* </p>
* @hide
*/
- public static final String EXTRA_TARGET_COMPONENT_NAME =
- "android.content.extra.TARGET_COMPONENT_NAME";
+ public static final String EXTRA_PENDING_INTENT = "android.intent.extra.PENDING_INTENT";
/**
- * The name of the extra used to define a user serial number when copying/dragging
- * an app icon from Launcher.
+ * The activity options bundle to use when launching an activity.
* <p>
- * Type: long
+ * Type: Bundle
* </p>
* @hide
*/
- public static final String EXTRA_USER_SERIAL_NUMBER =
- "android.content.extra.USER_SERIAL_NUMBER";
+ public static final String EXTRA_ACTIVITY_OPTIONS = "android.intent.extra.ACTIVITY_OPTIONS";
final CharSequence mLabel;
@@ -203,6 +218,24 @@
}
/**
+ * Check whether the clip description contains any of the given MIME types.
+ *
+ * @param targetMimeTypes The target MIME types. May use patterns.
+ * @return Returns true if at least one of the MIME types in the clip description matches at
+ * least one of the target MIME types, else false.
+ *
+ * @hide
+ */
+ public boolean hasMimeType(@NonNull String[] targetMimeTypes) {
+ for (String targetMimeType : targetMimeTypes) {
+ if (hasMimeType(targetMimeType)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Filter the clip description MIME types by the given MIME type. Returns
* all MIME types in the clip that match the given MIME type.
*
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 2fa0564..9216a08 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -619,7 +619,6 @@
* <li> {@link #EXTRA_PHONE_NUMBER}
* <li> {@link #EXTRA_REFERRER}
* <li> {@link #EXTRA_REMOTE_INTENT_TOKEN}
- * <li> {@link #EXTRA_REMOVED_BY_SYSTEM}
* <li> {@link #EXTRA_REPLACING}
* <li> {@link #EXTRA_SHORTCUT_ICON}
* <li> {@link #EXTRA_SHORTCUT_ICON_RESOURCE}
@@ -631,6 +630,7 @@
* <li> {@link #EXTRA_TEXT}
* <li> {@link #EXTRA_TITLE}
* <li> {@link #EXTRA_UID}
+ * <li> {@link #EXTRA_USER_INITIATED}
* </ul>
*
* <h3>Flags</h3>
@@ -2461,8 +2461,8 @@
* application -- data and code -- is being removed.
* <li> {@link #EXTRA_REPLACING} is set to true if this will be followed
* by an {@link #ACTION_PACKAGE_ADDED} broadcast for the same package.
- * <li> {@link #EXTRA_REMOVED_BY_SYSTEM} containing boolean field to to signal that the
- * application was removed automatically without the user-initiated action.
+ * <li> {@link #EXTRA_USER_INITIATED} containing boolean field to signal that the application
+ * was removed with the user-initiated action.
* </ul>
*
* <p class="note">This is a protected intent that can only be sent
@@ -5555,11 +5555,9 @@
/**
* Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED}
- * intents to signal that the application was removed automatically without the user-initiated
- * action.
+ * intents to signal that the application was removed with the user-initiated action.
*/
- public static final String EXTRA_REMOVED_BY_SYSTEM =
- "android.intent.extra.REMOVED_BY_SYSTEM";
+ public static final String EXTRA_USER_INITIATED = "android.intent.extra.USER_INITIATED";
/**
* A String holding the phone number originally entered in
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 389458b..d688614 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -17,13 +17,14 @@
package android.content.pm;
import android.app.IApplicationThread;
+import android.app.PendingIntent;
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.LauncherActivityInfoInternal;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutQueryWrapper;
import android.content.pm.IPackageInstallerCallback;
@@ -47,7 +48,7 @@
void removeOnAppsChangedListener(in IOnAppsChangedListener listener);
ParceledListSlice getLauncherActivities(
String callingPackage, String packageName, in UserHandle user);
- ActivityInfo resolveActivity(
+ LauncherActivityInfoInternal resolveLauncherActivityInternal(
String callingPackage, in ComponentName component, in UserHandle user);
void startSessionDetailsActivityAsUser(in IApplicationThread caller, String callingPackage,
String callingFeatureId, in PackageInstaller.SessionInfo sessionInfo,
@@ -55,6 +56,8 @@
void startActivityAsUser(in IApplicationThread caller, String callingPackage,
String callingFeatureId, in ComponentName component, in Rect sourceBounds,
in Bundle opts, in UserHandle user);
+ PendingIntent getActivityLaunchIntent(in ComponentName component, in Bundle opts,
+ in UserHandle user);
void showAppDetailsAsUser(in IApplicationThread caller, String callingPackage,
String callingFeatureId, in ComponentName component, in Rect sourceBounds,
in Bundle opts, in UserHandle user);
diff --git a/core/java/android/content/pm/IOnAppsChangedListener.aidl b/core/java/android/content/pm/IOnAppsChangedListener.aidl
index fcb1de0..f24ed80 100644
--- a/core/java/android/content/pm/IOnAppsChangedListener.aidl
+++ b/core/java/android/content/pm/IOnAppsChangedListener.aidl
@@ -33,4 +33,5 @@
in Bundle launcherExtras);
void onPackagesUnsuspended(in UserHandle user, in String[] packageNames);
void onShortcutChanged(in UserHandle user, String packageName, in ParceledListSlice shortcuts);
+ void onPackageProgressChanged(in UserHandle user, String packageName, float progress);
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 30f3325..106021e 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -31,7 +31,6 @@
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDeleteObserver2;
import android.content.pm.IPackageDataObserver;
-import android.content.pm.IPackageLoadingProgressCallback;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.IntentFilterVerificationInfo;
@@ -795,5 +794,7 @@
void grantImplicitAccess(int queryingUid, String visibleAuthority);
- void holdLock(in int durationMs);
+ IBinder getHoldLockToken();
+
+ void holdLock(in IBinder token, in int durationMs);
}
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index 67deb82..ead80d0 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -16,7 +16,6 @@
package android.content.pm;
-import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -35,28 +34,19 @@
private static final String TAG = "LauncherActivityInfo";
private final PackageManager mPm;
-
- @UnsupportedAppUsage
- private ActivityInfo mActivityInfo;
- private ComponentName mComponentName;
private UserHandle mUser;
+ private final LauncherActivityInfoInternal mInternal;
/**
* Create a launchable activity object for a given ResolveInfo and user.
*
* @param context The context for fetching resources.
- * @param info ResolveInfo from which to create the LauncherActivityInfo.
- * @param user The UserHandle of the profile to which this activity belongs.
- */
- LauncherActivityInfo(Context context, ActivityInfo info, UserHandle user) {
- this(context);
- mActivityInfo = info;
- mComponentName = new ComponentName(info.packageName, info.name);
- mUser = user;
- }
- LauncherActivityInfo(Context context) {
+ */
+ LauncherActivityInfo(Context context, UserHandle user, LauncherActivityInfoInternal internal) {
mPm = context.getPackageManager();
+ mUser = user;
+ mInternal = internal;
}
/**
@@ -65,7 +55,7 @@
* @return ComponentName of the activity
*/
public ComponentName getComponentName() {
- return mComponentName;
+ return mInternal.getComponentName();
}
/**
@@ -90,7 +80,28 @@
*/
public CharSequence getLabel() {
// TODO: Go through LauncherAppsService
- return mActivityInfo.loadLabel(mPm);
+ return mInternal.getActivityInfo().loadLabel(mPm);
+ }
+
+ /**
+ * @return whether the package is startable.
+ */
+ public boolean isStartable() {
+ return mInternal.getIncrementalStatesInfo().isStartable();
+ }
+
+ /**
+ * @return whether the package is still loading.
+ */
+ public boolean isLoading() {
+ return mInternal.getIncrementalStatesInfo().isLoading();
+ }
+
+ /**
+ * @return Package loading progress
+ */
+ public float getProgress() {
+ return mInternal.getIncrementalStatesInfo().getProgress();
}
/**
@@ -103,20 +114,20 @@
*/
public Drawable getIcon(int density) {
// TODO: Go through LauncherAppsService
- final int iconRes = mActivityInfo.getIconResource();
+ final int iconRes = mInternal.getActivityInfo().getIconResource();
Drawable icon = null;
// Get the preferred density icon from the app's resources
if (density != 0 && iconRes != 0) {
try {
- final Resources resources
- = mPm.getResourcesForApplication(mActivityInfo.applicationInfo);
+ final Resources resources = mPm.getResourcesForApplication(
+ mInternal.getActivityInfo().applicationInfo);
icon = resources.getDrawableForDensity(iconRes, density);
} catch (NameNotFoundException | Resources.NotFoundException exc) {
}
}
// Get the default density icon
if (icon == null) {
- icon = mActivityInfo.loadIcon(mPm);
+ icon = mInternal.getActivityInfo().loadIcon(mPm);
}
return icon;
}
@@ -128,7 +139,7 @@
* @hide remove before shipping
*/
public int getApplicationFlags() {
- return mActivityInfo.applicationInfo.flags;
+ return mInternal.getActivityInfo().flags;
}
/**
@@ -136,7 +147,7 @@
* @return
*/
public ApplicationInfo getApplicationInfo() {
- return mActivityInfo.applicationInfo;
+ return mInternal.getActivityInfo().applicationInfo;
}
/**
@@ -147,7 +158,7 @@
public long getFirstInstallTime() {
try {
// TODO: Go through LauncherAppsService
- return mPm.getPackageInfo(mActivityInfo.packageName,
+ return mPm.getPackageInfo(mInternal.getActivityInfo().packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES).firstInstallTime;
} catch (NameNotFoundException nnfe) {
// Sorry, can't find package
@@ -160,7 +171,7 @@
* @return the name from android:name for the acitivity.
*/
public String getName() {
- return mActivityInfo.name;
+ return mInternal.getActivityInfo().name;
}
/**
diff --git a/core/java/android/content/pm/LauncherActivityInfoInternal.aidl b/core/java/android/content/pm/LauncherActivityInfoInternal.aidl
new file mode 100644
index 0000000..5d98d54
--- /dev/null
+++ b/core/java/android/content/pm/LauncherActivityInfoInternal.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** 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 android.content.pm;
+
+parcelable LauncherActivityInfoInternal;
\ No newline at end of file
diff --git a/core/java/android/content/pm/LauncherActivityInfoInternal.java b/core/java/android/content/pm/LauncherActivityInfoInternal.java
new file mode 100644
index 0000000..417f168
--- /dev/null
+++ b/core/java/android/content/pm/LauncherActivityInfoInternal.java
@@ -0,0 +1,84 @@
+/*
+ * 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.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+public class LauncherActivityInfoInternal implements Parcelable {
+ @UnsupportedAppUsage
+ @NonNull private ActivityInfo mActivityInfo;
+ @NonNull private ComponentName mComponentName;
+ @NonNull private IncrementalStatesInfo mIncrementalStatesInfo;
+
+ /**
+ * @param info ActivityInfo from which to create the LauncherActivityInfo.
+ * @param incrementalStatesInfo The package's states.
+ */
+ public LauncherActivityInfoInternal(@NonNull ActivityInfo info,
+ @NonNull IncrementalStatesInfo incrementalStatesInfo) {
+ mActivityInfo = info;
+ mComponentName = new ComponentName(info.packageName, info.name);
+ mIncrementalStatesInfo = incrementalStatesInfo;
+ }
+
+ public LauncherActivityInfoInternal(Parcel source) {
+ mActivityInfo = source.readParcelable(ActivityInfo.class.getClassLoader());
+ mComponentName = new ComponentName(mActivityInfo.packageName, mActivityInfo.name);
+ mIncrementalStatesInfo = source.readParcelable(
+ IncrementalStatesInfo.class.getClassLoader());
+ }
+
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ public ActivityInfo getActivityInfo() {
+ return mActivityInfo;
+ }
+
+ public IncrementalStatesInfo getIncrementalStatesInfo() {
+ return mIncrementalStatesInfo;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mActivityInfo, 0);
+ dest.writeParcelable(mIncrementalStatesInfo, 0);
+ }
+
+ public static final @android.annotation.NonNull Creator<LauncherActivityInfoInternal> CREATOR =
+ new Creator<LauncherActivityInfoInternal>() {
+ public LauncherActivityInfoInternal createFromParcel(Parcel source) {
+ return new LauncherActivityInfoInternal(source);
+ }
+ public LauncherActivityInfoInternal[] newArray(int size) {
+ return new LauncherActivityInfoInternal[size];
+ }
+ };
+}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 1a694b3..2909d66 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -17,6 +17,7 @@
package android.content.pm;
import static android.Manifest.permission;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
@@ -218,6 +219,7 @@
* Indicates that a package was modified in the specified profile.
* This can happen, for example, when the package is updated or when
* one or more components are enabled or disabled.
+ * It can also happen if package state has changed, i.e., package becomes unstartable.
*
* @param packageName The name of the package that has changed.
* @param user The UserHandle of the profile that generated the change.
@@ -323,6 +325,16 @@
public void onShortcutsChanged(@NonNull String packageName,
@NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
}
+
+ /**
+ * Indicates that the loading progress of an installed package has changed.
+ *
+ * @param packageName The name of the package that has changed.
+ * @param user The UserHandle of the profile that generated the change.
+ * @param progress The new progress value, between [0, 1].
+ */
+ public void onPackageProgressChanged(@NonNull String packageName,
+ @NonNull UserHandle user, float progress) {}
}
/**
@@ -705,6 +717,29 @@
}
/**
+ * Returns a PendingIntent that would start the same activity started from
+ * {@link #startMainActivity(ComponentName, UserHandle, Rect, Bundle)}.
+ *
+ * @param component The ComponentName of the activity to launch
+ * @param startActivityOptions Options to pass to startActivity
+ * @param user The UserHandle of the profile
+ * @hide
+ */
+ @Nullable
+ public PendingIntent getMainActivityLaunchIntent(@NonNull ComponentName component,
+ @Nullable Bundle startActivityOptions, @NonNull UserHandle user) {
+ logErrorForInvalidProfileAccess(user);
+ if (DEBUG) {
+ Log.i(TAG, "GetMainActivityLaunchIntent " + component + " " + user);
+ }
+ try {
+ return mService.getActivityLaunchIntent(component, startActivityOptions, user);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
* returns null.
*
@@ -715,16 +750,15 @@
public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
logErrorForInvalidProfileAccess(user);
try {
- ActivityInfo ai = mService.resolveActivity(mContext.getPackageName(),
- intent.getComponent(), user);
- if (ai != null) {
- LauncherActivityInfo info = new LauncherActivityInfo(mContext, ai, user);
- return info;
+ LauncherActivityInfoInternal ai = mService.resolveLauncherActivityInternal(
+ mContext.getPackageName(), intent.getComponent(), user);
+ if (ai == null) {
+ return null;
}
+ return new LauncherActivityInfo(mContext, user, ai);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
- return null;
}
/**
@@ -813,13 +847,13 @@
}
private List<LauncherActivityInfo> convertToActivityList(
- @Nullable ParceledListSlice<ResolveInfo> activities, UserHandle user) {
- if (activities == null) {
+ @Nullable ParceledListSlice<LauncherActivityInfoInternal> internals, UserHandle user) {
+ if (internals == null || internals.getList().isEmpty()) {
return Collections.EMPTY_LIST;
}
ArrayList<LauncherActivityInfo> lais = new ArrayList<>();
- for (ResolveInfo ri : activities.getList()) {
- LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri.activityInfo, user);
+ for (LauncherActivityInfoInternal internal : internals.getList()) {
+ LauncherActivityInfo lai = new LauncherActivityInfo(mContext, user, internal);
if (DEBUG) {
Log.v(TAG, "Returning activity for profile " + user + " : "
+ lai.getComponentName());
@@ -1667,6 +1701,19 @@
}
}
}
+
+ public void onPackageProgressChanged(UserHandle user, String packageName,
+ float progress) {
+ if (DEBUG) {
+ Log.d(TAG, "onPackageProgressChanged " + user.getIdentifier() + ","
+ + packageName + "," + progress);
+ }
+ synchronized (LauncherApps.this) {
+ for (CallbackMessageHandler callback : mCallbacks) {
+ callback.postOnPackageProgressChanged(user, packageName, progress);
+ }
+ }
+ }
};
private static class CallbackMessageHandler extends Handler {
@@ -1678,6 +1725,7 @@
private static final int MSG_SUSPENDED = 6;
private static final int MSG_UNSUSPENDED = 7;
private static final int MSG_SHORTCUT_CHANGED = 8;
+ private static final int MSG_LOADING_PROGRESS_CHANGED = 9;
private LauncherApps.Callback mCallback;
@@ -1688,6 +1736,7 @@
boolean replacing;
UserHandle user;
List<ShortcutInfo> shortcuts;
+ float mLoadingProgress;
}
public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) {
@@ -1727,6 +1776,10 @@
case MSG_SHORTCUT_CHANGED:
mCallback.onShortcutsChanged(info.packageName, info.shortcuts, info.user);
break;
+ case MSG_LOADING_PROGRESS_CHANGED:
+ mCallback.onPackageProgressChanged(info.packageName, info.user,
+ info.mLoadingProgress);
+ break;
}
}
@@ -1793,6 +1846,15 @@
info.shortcuts = shortcuts;
obtainMessage(MSG_SHORTCUT_CHANGED, info).sendToTarget();
}
+
+ public void postOnPackageProgressChanged(UserHandle user, String packageName,
+ float progress) {
+ CallbackInfo info = new CallbackInfo();
+ info.packageName = packageName;
+ info.user = user;
+ info.mLoadingProgress = progress;
+ obtainMessage(MSG_LOADING_PROGRESS_CHANGED, info).sendToTarget();
+ }
}
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index fc7b3e0..602bd6c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -63,6 +63,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -301,7 +302,10 @@
/**
* {@link PackageInfo} flag: return information about the
* intent filters supported by the activity.
+ *
+ * @deprecated The platform does not support getting {@link IntentFilter}s for the package.
*/
+ @Deprecated
public static final int GET_INTENT_FILTERS = 0x00000020;
/**
@@ -1574,6 +1578,26 @@
*/
public static final int INSTALL_PARSE_FAILED_SKIPPED = -125;
+ /**
+ * Installation failed return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+ * because it is attempting to define a permission group that is already defined by some
+ * existing package.
+ *
+ * @hide
+ */
+ public static final int INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP = -126;
+
+ /**
+ * Installation failed return code: this is passed in the
+ * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the system failed to install the package
+ * because it is attempting to define a permission in a group that does not exists or that is
+ * defined by an packages with an incompatible certificate.
+ *
+ * @hide
+ */
+ public static final int INSTALL_FAILED_BAD_PERMISSION_GROUP = -127;
+
/** @hide */
@IntDef(flag = true, prefix = { "DELETE_" }, value = {
DELETE_KEEP_DATA,
@@ -6168,9 +6192,30 @@
public abstract Resources getResourcesForApplication(@NonNull String packageName)
throws NameNotFoundException;
- /** @hide */
+ /**
+ * Please don't use this function because it is no longer supported.
+ *
+ * @deprecated Instead of using this function, please use
+ * {@link Context#createContextAsUser(UserHandle, int)} to create the specified user
+ * context, {@link Context#getPackageManager()} to get PackageManager instance for
+ * the specified user, and then
+ * {@link PackageManager#getResourcesForApplication(String)} to get the same
+ * Resources instance.
+ * @see {@link Context#createContextAsUser(android.os.UserHandle, int)}
+ * @see {@link Context#getPackageManager()}
+ * @see {@link android.content.pm.PackageManager#getResourcesForApplication(java.lang.String)}
+ * TODO(b/170852794): mark maxTargetSdk as {@code Build.VERSION_CODES.S}
+ * @hide
+ */
@NonNull
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170928809,
+ publicAlternatives = "Use {@code Context#createContextAsUser(UserHandle, int)}"
+ + " to create the relevant user context,"
+ + " {@link android.content.Context#getPackageManager()} and"
+ + " {@link android.content.pm.PackageManager#getResourcesForApplication("
+ + "java.lang.String)}"
+ + " instead.")
+ @Deprecated
public abstract Resources getResourcesForApplicationAsUser(@NonNull String packageName,
@UserIdInt int userId) throws NameNotFoundException;
@@ -8418,15 +8463,30 @@
}
/**
+ * Returns the token to be used by the subsequent calls to holdLock().
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.INJECT_EVENTS)
+ @TestApi
+ public IBinder getHoldLockToken() {
+ try {
+ return ActivityThread.getPackageManager().getHoldLockToken();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Holds the PM lock for the specified amount of milliseconds.
* Intended for use by the tests that need to imitate lock contention.
+ * The token should be obtained by
+ * {@link android.content.pm.PackageManager#getHoldLockToken()}.
* @hide
*/
@TestApi
- @RequiresPermission(android.Manifest.permission.INJECT_EVENTS)
- public void holdLock(int durationMs) {
+ public void holdLock(IBinder token, int durationMs) {
try {
- ActivityThread.getPackageManager().holdLock(durationMs);
+ ActivityThread.getPackageManager().holdLock(token, durationMs);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 1b3c46f..7ed803f 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -630,7 +630,7 @@
* This will set {@link #FLAG_STRINGS_RESOLVED}.
*
* @param res {@link Resources} for the publisher. Must have been loaded with
- * {@link PackageManager#getResourcesForApplicationAsUser}.
+ * {@link PackageManager#getResourcesForApplication(String)}.
*
* @hide
*/
@@ -752,7 +752,7 @@
* aforementioned method would do internally, but not documented, so doing here explicitly.)
*
* @param res {@link Resources} for the publisher. Must have been loaded with
- * {@link PackageManager#getResourcesForApplicationAsUser}.
+ * {@link PackageManager#getResourcesForApplication(String)}.
*
* @hide
*/
@@ -782,7 +782,7 @@
* in the resource name fields.
*
* @param res {@link Resources} for the publisher. Must have been loaded with
- * {@link PackageManager#getResourcesForApplicationAsUser}.
+ * {@link PackageManager#getResourcesForApplication(String)}.
*
* @hide
*/
diff --git a/core/java/android/hardware/biometrics/BiometricTestSession.java b/core/java/android/hardware/biometrics/BiometricTestSession.java
index 802655b..2b68989 100644
--- a/core/java/android/hardware/biometrics/BiometricTestSession.java
+++ b/core/java/android/hardware/biometrics/BiometricTestSession.java
@@ -46,7 +46,7 @@
mContext = context;
mTestSession = testSession;
mTestedUsers = new ArraySet<>();
- enableTestHal(true);
+ setTestHalEnabled(true);
}
/**
@@ -56,12 +56,12 @@
* secure pathways such as HAT/Keystore are not testable, since they depend on the TEE or its
* equivalent for the secret key.
*
- * @param enableTestHal If true, enable testing with a fake HAL instead of the real HAL.
+ * @param enabled If true, enable testing with a fake HAL instead of the real HAL.
*/
@RequiresPermission(TEST_BIOMETRIC)
- private void enableTestHal(boolean enableTestHal) {
+ private void setTestHalEnabled(boolean enabled) {
try {
- mTestSession.enableTestHal(enableTestHal);
+ mTestSession.setTestHalEnabled(enabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -178,10 +178,12 @@
@Override
@RequiresPermission(TEST_BIOMETRIC)
public void close() {
+ // Disable the test HAL first, so that enumerate is run on the real HAL, which should have
+ // no enrollments. Test-only framework enrollments will be deleted.
+ setTestHalEnabled(false);
+
for (int user : mTestedUsers) {
cleanupInternalState(user);
}
-
- enableTestHal(false);
}
}
diff --git a/core/java/android/hardware/biometrics/ITestSession.aidl b/core/java/android/hardware/biometrics/ITestSession.aidl
index 6112f17..fa7a62c 100644
--- a/core/java/android/hardware/biometrics/ITestSession.aidl
+++ b/core/java/android/hardware/biometrics/ITestSession.aidl
@@ -27,7 +27,7 @@
// portion of the framework code that would otherwise require human interaction. Note that
// secure pathways such as HAT/Keystore are not testable, since they depend on the TEE or its
// equivalent for the secret key.
- void enableTestHal(boolean enableTestHal);
+ void setTestHalEnabled(boolean enableTestHal);
// Starts the enrollment process. This should generally be used when the test HAL is enabled.
void startEnroll(int userId);
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 2efec3f..62df92e 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1180,7 +1180,7 @@
* <p>The list of extended scene modes for {@link CaptureRequest#CONTROL_EXTENDED_SCENE_MODE android.control.extendedSceneMode} that
* are supported by this camera device, and each extended scene mode's capabilities such
* as maximum streaming size, and supported zoom ratio ranges.</p>
- * <p>For DISABLED mode, the camera behaves normally with no extended scene mdoe enabled.</p>
+ * <p>For DISABLED mode, the camera behaves normally with no extended scene mode enabled.</p>
* <p>For BOKEH_STILL_CAPTURE mode, the maximum streaming dimension specifies the limit
* under which bokeh is effective when capture intent is PREVIEW. Note that when capture
* intent is PREVIEW, the bokeh effect may not be as high quality compared to STILL_CAPTURE
@@ -3164,7 +3164,7 @@
* rectangle, and cropping to the rectangle given in {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</p>
* <p>E.g. to calculate position of a pixel, (x,y), in a processed YUV output image with the
* dimensions in {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} given the position of a pixel,
- * (x', y'), in the raw pixel array with dimensions give in
+ * (x', y'), in the raw pixel array with dimensions given in
* {@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize}:</p>
* <ol>
* <li>Choose a pixel (x', y') within the active array region of the raw buffer given in
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 8cfa086..228617c 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -190,6 +190,7 @@
}
}
+ private final String mCameraId;
@UnsupportedAppUsage
private final CameraMetadataNative mResults;
private final CaptureRequest mRequest;
@@ -202,7 +203,7 @@
* <p>For internal use only</p>
* @hide
*/
- public CaptureResult(CameraMetadataNative results, CaptureRequest parent,
+ public CaptureResult(String cameraId, CameraMetadataNative results, CaptureRequest parent,
CaptureResultExtras extras) {
if (results == null) {
throw new IllegalArgumentException("results was null");
@@ -221,6 +222,7 @@
throw new AssertionError("Results must not be empty");
}
setNativeInstance(mResults);
+ mCameraId = cameraId;
mRequest = parent;
mSequenceId = extras.getRequestId();
mFrameNumber = extras.getFrameNumber();
@@ -251,12 +253,27 @@
}
setNativeInstance(mResults);
+ mCameraId = "none";
mRequest = null;
mSequenceId = sequenceId;
mFrameNumber = -1;
}
/**
+ * Get the camera ID of the camera that produced this capture result.
+ *
+ * For a logical multi-camera, the ID may be the logical or the physical camera ID, depending on
+ * whether the capture result was obtained from
+ * {@link TotalCaptureResult#getPhysicalCameraResults} or not.
+ *
+ * @return The camera ID for the camera that produced this capture result.
+ */
+ @NonNull
+ public String getCameraId() {
+ return mCameraId;
+ }
+
+ /**
* Get a capture result field value.
*
* <p>The field definitions can be found in {@link CaptureResult}.</p>
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index 7cc2623..da65f71 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -70,10 +70,10 @@
* @param partials a list of partial results; {@code null} will be substituted for an empty list
* @hide
*/
- public TotalCaptureResult(CameraMetadataNative results, CaptureRequest parent,
- CaptureResultExtras extras, List<CaptureResult> partials, int sessionId,
- PhysicalCaptureResultInfo physicalResults[]) {
- super(results, parent, extras);
+ public TotalCaptureResult(String logicalCameraId, CameraMetadataNative results,
+ CaptureRequest parent, CaptureResultExtras extras, List<CaptureResult> partials,
+ int sessionId, PhysicalCaptureResultInfo[] physicalResults) {
+ super(logicalCameraId, results, parent, extras);
if (partials == null) {
mPartialResults = new ArrayList<>();
@@ -85,7 +85,7 @@
mPhysicalCaptureResults = new HashMap<String, CaptureResult>();
for (PhysicalCaptureResultInfo onePhysicalResult : physicalResults) {
- CaptureResult physicalResult = new CaptureResult(
+ CaptureResult physicalResult = new CaptureResult(onePhysicalResult.getCameraId(),
onePhysicalResult.getCameraMetadata(), parent, extras);
mPhysicalCaptureResults.put(onePhysicalResult.getCameraId(),
physicalResult);
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 48ec3fd..819d966 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -1980,7 +1980,7 @@
// Either send a partial result or the final capture completed result
if (isPartialResult) {
final CaptureResult resultAsCapture =
- new CaptureResult(result, request, resultExtras);
+ new CaptureResult(getId(), result, request, resultExtras);
// Partial result
resultDispatch = new Runnable() {
@Override
@@ -1992,7 +1992,7 @@
for (int i = 0; i < holder.getRequestCount(); i++) {
CameraMetadataNative resultLocal =
new CameraMetadataNative(resultCopy);
- CaptureResult resultInBatch = new CaptureResult(
+ CaptureResult resultInBatch = new CaptureResult(getId(),
resultLocal, holder.getRequest(i), resultExtras);
holder.getCallback().onCaptureProgressed(
@@ -2019,8 +2019,8 @@
final Range<Integer> fpsRange =
request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
final int subsequenceId = resultExtras.getSubsequenceId();
- final TotalCaptureResult resultAsCapture = new TotalCaptureResult(result,
- request, resultExtras, partialResults, holder.getSessionId(),
+ final TotalCaptureResult resultAsCapture = new TotalCaptureResult(getId(),
+ result, request, resultExtras, partialResults, holder.getSessionId(),
physicalResults);
// Final capture result
resultDispatch = new Runnable() {
@@ -2038,9 +2038,9 @@
new CameraMetadataNative(resultCopy);
// No logical multi-camera support for batched output mode.
TotalCaptureResult resultInBatch = new TotalCaptureResult(
- resultLocal, holder.getRequest(i), resultExtras,
- partialResults, holder.getSessionId(),
- new PhysicalCaptureResultInfo[0]);
+ getId(), resultLocal, holder.getRequest(i),
+ resultExtras, partialResults, holder.getSessionId(),
+ new PhysicalCaptureResultInfo[0]);
holder.getCallback().onCaptureCompleted(
CameraDeviceImpl.this,
diff --git a/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java
index 1d8b2a1..eb2ff88 100644
--- a/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java
@@ -334,7 +334,7 @@
// Either send a partial result or the final capture completed result
if (isPartialResult) {
final CaptureResult resultAsCapture =
- new CaptureResult(result, request, resultExtras);
+ new CaptureResult(mCameraId, result, request, resultExtras);
// Partial result
resultDispatch = new Runnable() {
@Override
@@ -349,7 +349,8 @@
CameraMetadataNative resultLocal =
new CameraMetadataNative(resultCopy);
final CaptureResult resultInBatch = new CaptureResult(
- resultLocal, holder.getRequest(i), resultExtras);
+ mCameraId, resultLocal, holder.getRequest(i),
+ resultExtras);
final CaptureRequest cbRequest = holder.getRequest(i);
callback.onCaptureProgressed(CameraOfflineSessionImpl.this,
@@ -372,8 +373,8 @@
final Range<Integer> fpsRange =
request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
final int subsequenceId = resultExtras.getSubsequenceId();
- final TotalCaptureResult resultAsCapture = new TotalCaptureResult(result,
- request, resultExtras, partialResults, holder.getSessionId(),
+ final TotalCaptureResult resultAsCapture = new TotalCaptureResult(mCameraId,
+ result, request, resultExtras, partialResults, holder.getSessionId(),
physicalResults);
// Final capture result
resultDispatch = new Runnable() {
@@ -393,9 +394,9 @@
new CameraMetadataNative(resultCopy);
// No logical multi-camera support for batched output mode.
TotalCaptureResult resultInBatch = new TotalCaptureResult(
- resultLocal, holder.getRequest(i), resultExtras,
- partialResults, holder.getSessionId(),
- new PhysicalCaptureResultInfo[0]);
+ mCameraId, resultLocal, holder.getRequest(i),
+ resultExtras, partialResults, holder.getSessionId(),
+ new PhysicalCaptureResultInfo[0]);
final CaptureRequest cbRequest = holder.getRequest(i);
callback.onCaptureCompleted(CameraOfflineSessionImpl.this,
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 5b186c7..401bb9d 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -319,6 +319,29 @@
/** The HdmiControlService will be disabled to standby. */
public static final int CONTROL_STATE_CHANGED_REASON_STANDBY = 3;
+ // -- Whether the HDMI CEC is enabled or disabled.
+ /**
+ * HDMI CEC enabled.
+ *
+ * @hide
+ */
+ public static final String HDMI_CEC_CONTROL_ENABLED = "1";
+ /**
+ * HDMI CEC disabled.
+ *
+ * @hide
+ */
+ public static final String HDMI_CEC_CONTROL_DISABLED = "0";
+ /**
+ * @hide
+ */
+ @StringDef({
+ HDMI_CEC_CONTROL_ENABLED,
+ HDMI_CEC_CONTROL_DISABLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface HdmiCecControl {}
+
// -- Which devices the playback device can send a <Standby> message to upon going to sleep.
/**
* Send <Standby> to TV only.
@@ -347,8 +370,91 @@
SEND_STANDBY_ON_SLEEP_NONE
})
@Retention(RetentionPolicy.SOURCE)
- public @interface StandbyBehavior {
- }
+ public @interface StandbyBehavior {}
+
+ // -- Which power state action should be taken when Active Source is lost.
+ /**
+ * No action to be taken.
+ *
+ * @hide
+ */
+ public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE = "none";
+ /**
+ * Go to standby immediately.
+ *
+ * @hide
+ */
+ public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW = "standby_now";
+ /**
+ * @hide
+ */
+ @StringDef({
+ POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE,
+ POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ActiveSourceLostBehavior {}
+
+ // -- Whether System Audio Mode muting is enabled or disabled.
+ /**
+ * System Audio Mode muting enabled.
+ *
+ * @hide
+ */
+ public static final String SYSTEM_AUDIO_MODE_MUTING_ENABLED = "1";
+ /**
+ * System Audio Mode muting disabled.
+ *
+ * @hide
+ */
+ public static final String SYSTEM_AUDIO_MODE_MUTING_DISABLED = "0";
+ /**
+ * @hide
+ */
+ @StringDef({
+ SYSTEM_AUDIO_MODE_MUTING_ENABLED,
+ SYSTEM_AUDIO_MODE_MUTING_DISABLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SystemAudioModeMuting {}
+
+ // -- Settings available in the CEC Configuration.
+ /**
+ * Name of a setting deciding whether the CEC is enabled.
+ *
+ * @hide
+ */
+ public static final String CEC_SETTING_NAME_HDMI_CEC_ENABLED = "hdmi_cec_enabled";
+ /**
+ * Name of a setting deciding on the Standby message behaviour on sleep.
+ *
+ * @hide
+ */
+ public static final String CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP = "send_standby_on_sleep";
+ /**
+ * Name of a setting deciding on power state action when losing Active Source.
+ *
+ * @hide
+ */
+ public static final String CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST =
+ "power_state_change_on_active_source_lost";
+ /**
+ * Name of a setting deciding whether System Audio Muting is allowed.
+ *
+ * @hide
+ */
+ public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING =
+ "system_audio_mode_muting";
+ /**
+ * @hide
+ */
+ @StringDef({
+ CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+ CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+ CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+ })
+ public @interface CecSettingName {}
// True if we have a logical device of type playback hosted in the system.
private final boolean mHasPlaybackDevice;
@@ -1186,4 +1292,234 @@
}
};
}
+
+ /**
+ * Get a set of user-modifiable settings.
+ *
+ * @return a set of user-modifiable settings.
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @NonNull
+ @CecSettingName
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public List<String> getUserCecSettings() {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ return mService.getUserCecSettings();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get a set of allowed values for a settings.
+ *
+ * @param name name of the setting
+ * @return a set of allowed values for a settings. {@code null} on failure.
+ * @throws IllegalArgumentException when setting {@code name} does not exist.
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @NonNull
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public List<String> getAllowedCecSettingValues(@NonNull @CecSettingName String name) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ return mService.getAllowedCecSettingValues(name);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set the 'hdmi_cec_enabled' option.
+ *
+ * @param value the desired value
+ * @throws IllegalArgumentException when the new value is not allowed.
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void setHdmiCecEnabled(@NonNull @HdmiCecControl String value) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ mService.setCecSettingValue(CEC_SETTING_NAME_HDMI_CEC_ENABLED, value);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the value of 'hdmi_cec_enabled' option.
+ *
+ * @return the current value.
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @NonNull
+ @HdmiCecControl
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public String getHdmiCecEnabled() {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ return mService.getCecSettingValue(CEC_SETTING_NAME_HDMI_CEC_ENABLED);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set the 'send_standby_on_sleep' option.
+ *
+ * @param value the desired value
+ * @throws IllegalArgumentException when the new value is not allowed.
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void setSendStandbyOnSleep(@NonNull @StandbyBehavior String value) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ mService.setCecSettingValue(CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, value);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the value of 'send_standby_on_sleep' option.
+ *
+ * @return the current value.
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @NonNull
+ @StandbyBehavior
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public String getSendStandbyOnSleep() {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ return mService.getCecSettingValue(CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set the 'power_state_change_on_active_source_lost' option.
+ *
+ * @param value the desired value
+ * @throws IllegalArgumentException when the new value is not allowed
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void setPowerStateChangeOnActiveSourceLost(
+ @NonNull @ActiveSourceLostBehavior String value) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ mService.setCecSettingValue(
+ CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, value);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the value of 'power_state_change_on_active_source_lost' option.
+ *
+ * @return the current value.
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @NonNull
+ @ActiveSourceLostBehavior
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public String getPowerStateChangeOnActiveSourceLost() {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ return mService.getCecSettingValue(
+ CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set the 'system_audio_mode_muting' option.
+ *
+ * @param value the desired value
+ * @throws IllegalArgumentException when the new value is not allowed.
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void setSystemAudioModeMuting(@NonNull @SystemAudioModeMuting String value) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ mService.setCecSettingValue(CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, value);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the value of 'system_audio_mode_muting' option.
+ *
+ * @return the current value.
+ * @throws RuntimeException when the HdmiControlService is not available.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemAudioModeMuting
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public String getSystemAudioModeMuting() {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ return mService.getCecSettingValue(CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
index 0289635..22d4640 100644
--- a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
+++ b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.TestApi;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -293,6 +294,26 @@
IHdmiCecVolumeControlFeatureListener listener) {
HdmiControlServiceWrapper.this.removeHdmiCecVolumeControlFeatureListener(listener);
}
+
+ @Override
+ public List<String> getUserCecSettings() {
+ return HdmiControlServiceWrapper.this.getUserCecSettings();
+ }
+
+ @Override
+ public List<String> getAllowedCecSettingValues(String name) {
+ return HdmiControlServiceWrapper.this.getAllowedCecSettingValues(name);
+ }
+
+ @Override
+ public String getCecSettingValue(String name) {
+ return HdmiControlServiceWrapper.this.getCecSettingValue(name);
+ }
+
+ @Override
+ public void setCecSettingValue(String name, String value) {
+ HdmiControlServiceWrapper.this.setCecSettingValue(name, value);
+ }
};
@BinderThread
@@ -466,4 +487,22 @@
/** @hide */
public void removeHdmiCecVolumeControlFeatureListener(
IHdmiCecVolumeControlFeatureListener listener) {}
+
+ /** @hide */
+ public List<String> getUserCecSettings() {
+ return new ArrayList<>();
+ }
+
+ /** @hide */
+ public List<String> getAllowedCecSettingValues(String name) {
+ return new ArrayList<>();
+ }
+
+ /** @hide */
+ public String getCecSettingValue(String name) {
+ return "";
+ }
+
+ /** @hide */
+ public void setCecSettingValue(String name, String value) {}
}
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 4c724ef..6df164b 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -87,4 +87,8 @@
boolean isHdmiCecVolumeControlEnabled();
void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute);
void setSystemAudioModeOnForAudioOnlySource();
+ List<String> getUserCecSettings();
+ List<String> getAllowedCecSettingValues(String name);
+ String getCecSettingValue(String name);
+ void setCecSettingValue(String name, String value);
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 25fec32..e9b6857 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -194,7 +194,7 @@
*/
@BlockUntrustedTouchesMode
public static final int DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE =
- BlockUntrustedTouchesMode.DISABLED;
+ BlockUntrustedTouchesMode.PERMISSIVE;
/**
* Prevent touches from being consumed by apps if these touches passed through a non-trusted
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index be33f4e..12ddc62 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -170,6 +170,7 @@
NET_CAPABILITY_MCX,
NET_CAPABILITY_PARTIAL_CONNECTIVITY,
NET_CAPABILITY_TEMPORARILY_NOT_METERED,
+ NET_CAPABILITY_OEM_PRIVATE,
})
public @interface NetCapability { }
@@ -345,8 +346,15 @@
*/
public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25;
+ /**
+ * Indicates that this network is private to the OEM and meant only for OEM use.
+ * @hide
+ */
+ @SystemApi
+ public static final int NET_CAPABILITY_OEM_PRIVATE = 26;
+
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_TEMPORARILY_NOT_METERED;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_OEM_PRIVATE;
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -404,7 +412,8 @@
* {@see #maybeMarkCapabilitiesRestricted}.
*/
private static final long FORCE_RESTRICTED_CAPABILITIES =
- (1 << NET_CAPABILITY_OEM_PAID);
+ (1 << NET_CAPABILITY_OEM_PAID)
+ | (1 << NET_CAPABILITY_OEM_PRIVATE);
/**
* Capabilities that suggest that a network is unrestricted.
@@ -1910,6 +1919,7 @@
case NET_CAPABILITY_MCX: return "MCX";
case NET_CAPABILITY_PARTIAL_CONNECTIVITY: return "PARTIAL_CONNECTIVITY";
case NET_CAPABILITY_TEMPORARILY_NOT_METERED: return "TEMPORARILY_NOT_METERED";
+ case NET_CAPABILITY_OEM_PRIVATE: return "OEM_PRIVATE";
default: return Integer.toString(capability);
}
}
diff --git a/core/java/android/net/vcn/OWNERS b/core/java/android/net/vcn/OWNERS
new file mode 100644
index 0000000..33b9f0f
--- /dev/null
+++ b/core/java/android/net/vcn/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+
+benedictwong@google.com
+ckesting@google.com
+evitayan@google.com
+nharold@google.com
+jchalard@google.com
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java b/core/java/android/os/CombinedVibrationEffect.aidl
similarity index 77%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java
rename to core/java/android/os/CombinedVibrationEffect.aidl
index 273bd27..330733c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java
+++ b/core/java/android/os/CombinedVibrationEffect.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 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.
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-package com.android.wm.shell;
+package android.os;
-/**
- * Interface for the shell.
- */
-public class WindowManagerShell {
-}
+parcelable CombinedVibrationEffect;
diff --git a/core/java/android/os/CombinedVibrationEffect.java b/core/java/android/os/CombinedVibrationEffect.java
new file mode 100644
index 0000000..77bfa57
--- /dev/null
+++ b/core/java/android/os/CombinedVibrationEffect.java
@@ -0,0 +1,150 @@
+/*
+ * 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.os;
+
+import android.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * A CombinedVibrationEffect describes a haptic effect to be performed by one or more {@link
+ * Vibrator Vibrators}.
+ *
+ * These effects may be any number of things, from single shot vibrations to complex waveforms.
+ *
+ * @hide
+ * @see VibrationEffect
+ */
+public abstract class CombinedVibrationEffect implements Parcelable {
+ private static final int PARCEL_TOKEN_MONO = 1;
+
+ /** @hide to prevent subclassing from outside of the framework */
+ public CombinedVibrationEffect() {
+ }
+
+ /**
+ * Create a synced vibration effect.
+ *
+ * A synced vibration effect should be performed by multiple vibrators at the same time.
+ *
+ * @param effect The {@link VibrationEffect} to perform
+ * @return The desired combined effect.
+ */
+ @NonNull
+ public static CombinedVibrationEffect createSynced(@NonNull VibrationEffect effect) {
+ CombinedVibrationEffect combined = new Mono(effect);
+ combined.validate();
+ return combined;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ public abstract void validate();
+
+ /**
+ * Represents a single {@link VibrationEffect} that should be executed in all vibrators in sync.
+ *
+ * @hide
+ */
+ public static final class Mono extends CombinedVibrationEffect {
+ private final VibrationEffect mEffect;
+
+ public Mono(Parcel in) {
+ mEffect = VibrationEffect.CREATOR.createFromParcel(in);
+ }
+
+ public Mono(@NonNull VibrationEffect effect) {
+ mEffect = effect;
+ }
+
+ public VibrationEffect getEffect() {
+ return mEffect;
+ }
+
+ /** @hide */
+ @Override
+ public void validate() {
+ mEffect.validate();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof CombinedVibrationEffect.Mono)) {
+ return false;
+ }
+ CombinedVibrationEffect.Mono other = (CombinedVibrationEffect.Mono) o;
+ return other.mEffect.equals(other.mEffect);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mEffect);
+ }
+
+ @Override
+ public String toString() {
+ return "Mono{mEffect=" + mEffect + '}';
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(PARCEL_TOKEN_MONO);
+ mEffect.writeToParcel(out, flags);
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<Mono> CREATOR =
+ new Parcelable.Creator<Mono>() {
+ @Override
+ public Mono createFromParcel(@NonNull Parcel in) {
+ // Skip the type token
+ in.readInt();
+ return new Mono(in);
+ }
+
+ @Override
+ @NonNull
+ public Mono[] newArray(int size) {
+ return new Mono[size];
+ }
+ };
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<CombinedVibrationEffect> CREATOR =
+ new Parcelable.Creator<CombinedVibrationEffect>() {
+ @Override
+ public CombinedVibrationEffect createFromParcel(Parcel in) {
+ int token = in.readInt();
+ if (token == PARCEL_TOKEN_MONO) {
+ return new CombinedVibrationEffect.Mono(in);
+ } else {
+ throw new IllegalStateException(
+ "Unexpected combined vibration event type token in parcel.");
+ }
+ }
+
+ @Override
+ public CombinedVibrationEffect[] newArray(int size) {
+ return new CombinedVibrationEffect[size];
+ }
+ };
+}
diff --git a/core/java/android/os/IVibratorManagerService.aidl b/core/java/android/os/IVibratorManagerService.aidl
index e821e31..08d2019 100644
--- a/core/java/android/os/IVibratorManagerService.aidl
+++ b/core/java/android/os/IVibratorManagerService.aidl
@@ -16,9 +16,13 @@
package android.os;
+import android.os.CombinedVibrationEffect;
import android.os.VibrationAttributes;
/** {@hide} */
interface IVibratorManagerService {
int[] getVibratorIds();
+ void vibrate(int uid, String opPkg, in CombinedVibrationEffect effect,
+ in VibrationAttributes attributes, String reason, IBinder token);
+ void cancelVibrate(IBinder token);
}
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index c39fd4d..8c98362 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -44,7 +44,7 @@
* public void run() {
* Looper.prepare();
*
- * mHandler = new Handler() {
+ * mHandler = new Handler(Looper.myLooper()) {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
diff --git a/core/java/android/os/OutcomeReceiver.java b/core/java/android/os/OutcomeReceiver.java
new file mode 100644
index 0000000..01b2764
--- /dev/null
+++ b/core/java/android/os/OutcomeReceiver.java
@@ -0,0 +1,42 @@
+/*
+ * 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.os;
+
+import android.annotation.NonNull;
+
+/**
+ * Callback interface intended for use when an asynchronous operation may result in a failure.
+ *
+ * This interface may be used in cases where an asynchronous API may complete either with a value
+ * or with a {@link Throwable} that indicates an error.
+ * @param <R> The type of the result that's being sent.
+ * @param <E> The type of the {@link Throwable} that contains more information about the error.
+ */
+public interface OutcomeReceiver<R, E extends Throwable> {
+ /**
+ * Called when the asynchronous operation succeeds and delivers a result value.
+ * @param result The value delivered by the asynchronous operation.
+ */
+ void onResult(@NonNull R result);
+
+ /**
+ * Called when the asynchronous operation fails. The mode of failure is indicated by the
+ * {@link Throwable} passed as an argument to this method.
+ * @param error A subclass of {@link Throwable} with more details about the error that occurred.
+ */
+ default void onError(@NonNull E error) {}
+}
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 8cdcd49..13b30f4 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -908,7 +908,11 @@
Intent intent = new Intent(ACTION_EUICC_FACTORY_RESET);
intent.setPackage(packageName);
PendingIntent callbackIntent = PendingIntent.getBroadcastAsUser(
- context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT, UserHandle.SYSTEM);
+ context,
+ 0,
+ intent,
+ PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT,
+ UserHandle.SYSTEM);
IntentFilter filterConsent = new IntentFilter();
filterConsent.addAction(ACTION_EUICC_FACTORY_RESET);
HandlerThread euiccHandlerThread = new HandlerThread("euiccWipeFinishReceiverThread");
@@ -1002,7 +1006,11 @@
Intent intent = new Intent(ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS);
intent.setPackage(PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK);
PendingIntent callbackIntent = PendingIntent.getBroadcastAsUser(
- context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT, UserHandle.SYSTEM);
+ context,
+ 0,
+ intent,
+ PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT,
+ UserHandle.SYSTEM);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS);
HandlerThread euiccHandlerThread =
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 487e468..21ad38b 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -983,6 +983,8 @@
Composition.checkPrimitive(effect.id);
Preconditions.checkArgumentInRange(
effect.scale, 0.0f, 1.0f, "scale");
+ Preconditions.checkArgumentNonNegative(effect.delay,
+ "Primitive delay must be zero or positive");
}
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index c319e85..e4220dd 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -16,6 +16,8 @@
package android.permission;
+import static android.os.Build.VERSION_CODES.S;
+
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntRange;
@@ -29,6 +31,8 @@
import android.app.ActivityThread;
import android.app.IActivityManager;
import android.app.PropertyInvalidatedCache;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
@@ -68,6 +72,17 @@
public static final String KILL_APP_REASON_GIDS_CHANGED =
"permission grant or revoke changed gids";
+ /**
+ * Refuse to install package if groups of permissions are bad
+ * - Permission groups should only be shared between apps sharing a certificate
+ * - If a permission belongs to a group that group should be defined
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = S)
+ public static final long CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS = 146211400;
+
private final @NonNull Context mContext;
private final IPackageManager mPackageManager;
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index c3b6d8e..105ffaa 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -17,6 +17,7 @@
package android.provider;
+import android.annotation.LongDef;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -43,6 +44,8 @@
import android.text.TextUtils;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
@@ -611,6 +614,144 @@
*/
public static final String BLOCK_REASON = "block_reason";
+ /** @hide */
+ @LongDef(flag = true, value = {
+ MISSED_REASON_NOT_MISSED,
+ AUTO_MISSED_EMERGENCY_CALL,
+ AUTO_MISSED_MAXIMUM_RINGING,
+ AUTO_MISSED_MAXIMUM_DIALING,
+ USER_MISSED_NO_ANSWER,
+ USER_MISSED_SHORT_RING,
+ USER_MISSED_DND_MODE,
+ USER_MISSED_LOW_RING_VOLUME,
+ USER_MISSED_NO_VIBRATE,
+ USER_MISSED_CALL_SCREENING_SERVICE_SILENCED,
+ USER_MISSED_CALL_FILTERS_TIMEOUT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MissedReason {}
+
+ /**
+ * Value for {@link CallLog.Calls#MISSED_REASON}, set as the default value when a call was
+ * not missed.
+ */
+ public static final long MISSED_REASON_NOT_MISSED = 0;
+
+ /**
+ * Value for {@link CallLog.Calls#MISSED_REASON}, set when {@link CallLog.Calls#TYPE} is
+ * {@link CallLog.Calls#MISSED_TYPE} to indicate that a call was automatically rejected by
+ * system because an ongoing emergency call.
+ */
+ public static final long AUTO_MISSED_EMERGENCY_CALL = 1 << 0;
+
+ /**
+ * Value for {@link CallLog.Calls#MISSED_REASON}, set when {@link CallLog.Calls#TYPE} is
+ * {@link CallLog.Calls#MISSED_TYPE} to indicate that a call was automatically rejected by
+ * system because the system cannot support any more ringing calls.
+ */
+ public static final long AUTO_MISSED_MAXIMUM_RINGING = 1 << 1;
+
+ /**
+ * Value for {@link CallLog.Calls#MISSED_REASON}, set when {@link CallLog.Calls#TYPE} is
+ * {@link CallLog.Calls#MISSED_TYPE} to indicate that a call was automatically rejected by
+ * system because the system cannot support any more dialing calls.
+ */
+ public static final long AUTO_MISSED_MAXIMUM_DIALING = 1 << 2;
+
+ /**
+ * When {@link CallLog.Calls#TYPE} is {@link CallLog.Calls#MISSED_TYPE}, set this bit when
+ * the call was missed just because user didn't answer it.
+ */
+ public static final long USER_MISSED_NO_ANSWER = 1 << 16;
+
+ /**
+ * When {@link CallLog.Calls#TYPE} is {@link CallLog.Calls#MISSED_TYPE}, set this bit when
+ * this call rang for a short period of time.
+ */
+ public static final long USER_MISSED_SHORT_RING = 1 << 17;
+
+ /**
+ * When {@link CallLog.Calls#TYPE} is {@link CallLog.Calls#MISSED_TYPE}, when this call
+ * rings less than this defined time in millisecond, set
+ * {@link CallLog.Calls#USER_MISSED_SHORT_RING} bit.
+ * @hide
+ */
+ public static final long SHORT_RING_THRESHOLD = 5000L;
+
+ /**
+ * When {@link CallLog.Calls#TYPE} is {@link CallLog.Calls#MISSED_TYPE}, set this bit when
+ * this call is silenced because the phone is in 'do not disturb mode'.
+ */
+ public static final long USER_MISSED_DND_MODE = 1 << 18;
+
+ /**
+ * When {@link CallLog.Calls#TYPE} is {@link CallLog.Calls#MISSED_TYPE}, set this bit when
+ * this call rings with a low ring volume.
+ */
+ public static final long USER_MISSED_LOW_RING_VOLUME = 1 << 19;
+
+ /**
+ * When {@link CallLog.Calls#TYPE} is {@link CallLog.Calls#MISSED_TYPE}, when this call
+ * rings in volume less than this defined volume threshold, set
+ * {@link CallLog.Calls#USER_MISSED_LOW_RING_VOLUME} bit.
+ * @hide
+ */
+ public static final int LOW_RING_VOLUME = 0;
+
+ /**
+ * When {@link CallLog.Calls#TYPE} is {@link CallLog.Calls#MISSED_TYPE} set this bit when
+ * this call rings without vibration.
+ */
+ public static final long USER_MISSED_NO_VIBRATE = 1 << 20;
+
+ /**
+ * When {@link CallLog.Calls#TYPE} is {@link CallLog.Calls#MISSED_TYPE}, set this bit when
+ * this call is silenced by the call screening service.
+ */
+ public static final long USER_MISSED_CALL_SCREENING_SERVICE_SILENCED = 1 << 21;
+
+ /**
+ * When {@link CallLog.Calls#TYPE} is {@link CallLog.Calls#MISSED_TYPE}, set this bit when
+ * the call filters timed out.
+ */
+ public static final long USER_MISSED_CALL_FILTERS_TIMEOUT = 1 << 22;
+
+ /**
+ * Where the {@link CallLog.Calls#TYPE} is {@link CallLog.Calls#MISSED_TYPE},
+ * indicates factors which may have lead the user to miss the call.
+ * <P>Type: INTEGER</P>
+ *
+ * <p>
+ * There are two main cases. Auto missed cases and user missed cases. Default value is:
+ * <ul>
+ * <li>{@link CallLog.Calls#MISSED_REASON_NOT_MISSED}</li>
+ * </ul>
+ * </p>
+ * <P>
+ * Auto missed cases are those where a call was missed because it was not possible for the
+ * incoming call to be presented to the user at all. Possible values are:
+ * <ul>
+ * <li>{@link CallLog.Calls#AUTO_MISSED_EMERGENCY_CALL}</li>
+ * <li>{@link CallLog.Calls#AUTO_MISSED_MAXIMUM_RINGING}</li>
+ * <li>{@link CallLog.Calls#AUTO_MISSED_MAXIMUM_DIALING}</li>
+ * </ul>
+ * </P>
+ * <P>
+ * User missed cases are those where the incoming call was presented to the user, but
+ * factors such as a low ringing volume may have contributed to the call being missed.
+ * Following bits can be set to indicate possible reasons for this:
+ * <ul>
+ * <li>{@link CallLog.Calls#USER_MISSED_SHORT_RING}</li>
+ * <li>{@link CallLog.Calls#USER_MISSED_DND_MODE}</li>
+ * <li>{@link CallLog.Calls#USER_MISSED_LOW_RING_VOLUME}</li>
+ * <li>{@link CallLog.Calls#USER_MISSED_NO_VIBRATE}</li>
+ * <li>{@link CallLog.Calls#USER_MISSED_CALL_SCREENING_SERVICE_SILENCED}</li>
+ * <li>{@link CallLog.Calls#USER_MISSED_CALL_FILTERS_TIMEOUT}</li>
+ * </ul>
+ * </P>
+ */
+ public static final String MISSED_REASON = "missed_reason";
+
/**
* Adds a call to the call log.
*
@@ -635,12 +776,13 @@
public static Uri addCall(CallerInfo ci, Context context, String number,
int presentation, int callType, int features,
PhoneAccountHandle accountHandle,
- long start, int duration, Long dataUsage) {
+ long start, int duration, Long dataUsage, long missedReason) {
return addCall(ci, context, number, "" /* postDialDigits */, "" /* viaNumber */,
presentation, callType, features, accountHandle, start, duration,
dataUsage, false /* addForAllUsers */, null /* userToBeInsertedTo */,
false /* isRead */, Calls.BLOCK_REASON_NOT_BLOCKED /* callBlockReason */,
- null /* callScreeningAppName */, null /* callScreeningComponentName */);
+ null /* callScreeningAppName */, null /* callScreeningComponentName */,
+ missedReason);
}
@@ -675,12 +817,13 @@
public static Uri addCall(CallerInfo ci, Context context, String number,
String postDialDigits, String viaNumber, int presentation, int callType,
int features, PhoneAccountHandle accountHandle, long start, int duration,
- Long dataUsage, boolean addForAllUsers, UserHandle userToBeInsertedTo) {
+ Long dataUsage, boolean addForAllUsers, UserHandle userToBeInsertedTo,
+ long missedReason) {
return addCall(ci, context, number, postDialDigits, viaNumber, presentation, callType,
features, accountHandle, start, duration, dataUsage, addForAllUsers,
userToBeInsertedTo, false /* isRead */ , Calls.BLOCK_REASON_NOT_BLOCKED
/* callBlockReason */, null /* callScreeningAppName */,
- null /* callScreeningComponentName */);
+ null /* callScreeningComponentName */, missedReason);
}
/**
@@ -714,6 +857,7 @@
* @param callBlockReason The reason why the call is blocked.
* @param callScreeningAppName The call screening application name which block the call.
* @param callScreeningComponentName The call screening component name which block the call.
+ * @param missedReason The encoded missed information of the call.
*
* @result The URI of the call log entry belonging to the user that made or received this
* call. This could be of the shadow provider. Do not return it to non-system apps,
@@ -726,7 +870,7 @@
int features, PhoneAccountHandle accountHandle, long start, int duration,
Long dataUsage, boolean addForAllUsers, UserHandle userToBeInsertedTo,
boolean isRead, int callBlockReason, CharSequence callScreeningAppName,
- String callScreeningComponentName) {
+ String callScreeningComponentName, long missedReason) {
if (VERBOSE_LOG) {
Log.v(LOG_TAG, String.format("Add call: number=%s, user=%s, for all=%s",
number, userToBeInsertedTo, addForAllUsers));
@@ -779,6 +923,7 @@
values.put(BLOCK_REASON, callBlockReason);
values.put(CALL_SCREENING_APP_NAME, charSequenceToString(callScreeningAppName));
values.put(CALL_SCREENING_COMPONENT_NAME, callScreeningComponentName);
+ values.put(MISSED_REASON, Long.valueOf(missedReason));
if ((ci != null) && (ci.getContactId() > 0)) {
// Update usage information for the number associated with the contact ID.
@@ -1114,5 +1259,19 @@
}
return countryIso;
}
+
+ /**
+ * Check if the missedReason code indicate that the call was user missed or automatically
+ * rejected by system.
+ *
+ * @param missedReason
+ * The result is true if the call was user missed, false if the call was automatically
+ * rejected by system.
+ *
+ * @hide
+ */
+ public static boolean isUserMissed(long missedReason) {
+ return missedReason >= (USER_MISSED_NO_ANSWER);
+ }
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0e3708e..9c41886 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8380,18 +8380,19 @@
public static final String CAMERA_GESTURE_DISABLED = "camera_gesture_disabled";
/**
- * Whether the panic button (emergency sos) gesture should be enabled.
+ * Whether the emergency gesture should be enabled.
*
* @hide
*/
- public static final String PANIC_GESTURE_ENABLED = "panic_gesture_enabled";
+ public static final String EMERGENCY_GESTURE_ENABLED = "emergency_gesture_enabled";
/**
- * Whether the panic button (emergency sos) sound should be enabled.
+ * Whether the emergency gesture sound should be enabled.
*
* @hide
*/
- public static final String PANIC_SOUND_ENABLED = "panic_sound_enabled";
+ public static final String EMERGENCY_GESTURE_SOUND_ENABLED =
+ "emergency_gesture_sound_enabled";
/**
* Whether the camera launch gesture to double tap the power button when the screen is off
@@ -8936,12 +8937,6 @@
"packages_to_clear_data_before_full_restore";
/**
- * Setting to determine whether to use the new notification priority handling features.
- * @hide
- */
- public static final String NOTIFICATION_NEW_INTERRUPTION_MODEL = "new_interruption_model";
-
- /**
* How often to check for location access.
* @hide
*/
@@ -9920,13 +9915,19 @@
"hdmi_control_auto_device_off_enabled";
/**
- * Property to decide which devices the playback device can send a <Standby> message to upon
- * going to sleep. Supported values are:
+ * Property to decide which devices the playback device can send a <Standby> message to
+ * upon going to sleep. It additionally controls whether a playback device attempts to turn
+ * on the connected Audio system when waking up. Supported values are:
* <ul>
- * <li>{@link HdmiControlManager#SEND_STANDBY_ON_SLEEP_TO_TV} to TV only.</li>
- * <li>{@link HdmiControlManager#SEND_STANDBY_ON_SLEEP_BROADCAST} to all devices in the
- * network.</li>
- * <li>{@link HdmiControlManager#SEND_STANDBY_ON_SLEEP_NONE} no <Standby> message sent.</li>
+ * <li>{@link HdmiControlManager#SEND_STANDBY_ON_SLEEP_TO_TV} Upon going to sleep, device
+ * sends {@code <Standby>} to TV only. Upon waking up, device does not turn on the Audio
+ * system via {@code <System Audio Mode Request>}.</li>
+ * <li>{@link HdmiControlManager#SEND_STANDBY_ON_SLEEP_BROADCAST} Upon going to sleep,
+ * device sends {@code <Standby>} to all devices in the network. Upon waking up, device
+ * attempts to turn on the Audio system via {@code <System Audio Mode Request>}.</li>
+ * <li>{@link HdmiControlManager#SEND_STANDBY_ON_SLEEP_NONE} Upon going to sleep, device
+ * does not send any {@code <Standby>} message. Upon waking up, device does not turn on the
+ * Audio system via {@code <System Audio Mode Request>}.</li>
* </ul>
*
* @hide
@@ -10434,14 +10435,6 @@
"webview_data_reduction_proxy_key";
/**
- * Whether or not the WebView fallback mechanism should be enabled.
- * 0=disabled, 1=enabled.
- * @hide
- */
- public static final String WEBVIEW_FALLBACK_LOGIC_ENABLED =
- "webview_fallback_logic_enabled";
-
- /**
* Name of the package used as WebView provider (if unset the provider is instead determined
* by the system).
* @hide
@@ -11895,21 +11888,6 @@
public static final String POWER_MANAGER_CONSTANTS = "power_manager_constants";
/**
- * Job scheduler QuotaController specific settings.
- * This is encoded as a key=value list, separated by commas. Ex:
- *
- * "max_job_count_working=5,max_job_count_rare=2"
- *
- * <p>
- * Type: string
- *
- * @hide
- * @see com.android.server.job.JobSchedulerService.Constants
- */
- public static final String JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS =
- "job_scheduler_quota_controller_constants";
-
- /**
* ShortcutManager specific settings.
* This is encoded as a key=value list, separated by commas. Ex:
*
diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java
index 6bd376a..c5277ee 100644
--- a/core/java/android/service/controls/ControlsProviderService.java
+++ b/core/java/android/service/controls/ControlsProviderService.java
@@ -206,8 +206,8 @@
case MSG_SUBSCRIBE: {
final SubscribeMessage sMsg = (SubscribeMessage) msg.obj;
- final SubscriberProxy proxy = new SubscriberProxy(false, mToken,
- sMsg.mSubscriber);
+ final SubscriberProxy proxy = new SubscriberProxy(
+ ControlsProviderService.this, false, mToken, sMsg.mSubscriber);
ControlsProviderService.this.createPublisherFor(sMsg.mControlIds)
.subscribe(proxy);
@@ -251,6 +251,7 @@
private IBinder mToken;
private IControlsSubscriber mCs;
private boolean mEnforceStateless;
+ private Context mContext;
SubscriberProxy(boolean enforceStateless, IBinder token, IControlsSubscriber cs) {
mEnforceStateless = enforceStateless;
@@ -258,6 +259,12 @@
mCs = cs;
}
+ SubscriberProxy(Context context, boolean enforceStateless, IBinder token,
+ IControlsSubscriber cs) {
+ this(enforceStateless, token, cs);
+ mContext = context;
+ }
+
public void onSubscribe(Subscription subscription) {
try {
mCs.onSubscribe(mToken, new SubscriptionAdapter(subscription));
@@ -273,6 +280,9 @@
+ "Control.StatelessBuilder() to build the control.");
control = new Control.StatelessBuilder(control).build();
}
+ if (mContext != null) {
+ control.getControlTemplate().prepareTemplateForBinder(mContext);
+ }
mCs.onNext(mToken, control);
} catch (RemoteException ex) {
ex.rethrowAsRuntimeException();
diff --git a/core/java/android/service/controls/templates/ControlTemplate.java b/core/java/android/service/controls/templates/ControlTemplate.java
index e592fad..3902d6a 100644
--- a/core/java/android/service/controls/templates/ControlTemplate.java
+++ b/core/java/android/service/controls/templates/ControlTemplate.java
@@ -20,6 +20,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.os.Bundle;
import android.service.controls.Control;
import android.service.controls.actions.ControlAction;
@@ -78,6 +79,7 @@
TYPE_NO_TEMPLATE,
TYPE_TOGGLE,
TYPE_RANGE,
+ TYPE_THUMBNAIL,
TYPE_TOGGLE_RANGE,
TYPE_TEMPERATURE,
TYPE_STATELESS
@@ -105,6 +107,11 @@
public static final @TemplateType int TYPE_RANGE = 2;
/**
+ * Type identifier of {@link ThumbnailTemplate}.
+ */
+ public static final @TemplateType int TYPE_THUMBNAIL = 3;
+
+ /**
* Type identifier of {@link ToggleRangeTemplate}.
*/
public static final @TemplateType int TYPE_TOGGLE_RANGE = 6;
@@ -169,6 +176,13 @@
}
/**
+ * Call to prepare values for Binder transport.
+ *
+ * @hide
+ */
+ public void prepareTemplateForBinder(@NonNull Context context) {}
+
+ /**
*
* @param bundle
* @return
@@ -187,6 +201,8 @@
return new ToggleTemplate(bundle);
case TYPE_RANGE:
return new RangeTemplate(bundle);
+ case TYPE_THUMBNAIL:
+ return new ThumbnailTemplate(bundle);
case TYPE_TOGGLE_RANGE:
return new ToggleRangeTemplate(bundle);
case TYPE_TEMPERATURE:
diff --git a/core/java/android/service/controls/templates/ThumbnailTemplate.java b/core/java/android/service/controls/templates/ThumbnailTemplate.java
new file mode 100644
index 0000000..a7c481e
--- /dev/null
+++ b/core/java/android/service/controls/templates/ThumbnailTemplate.java
@@ -0,0 +1,134 @@
+/*
+ * 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.service.controls.templates;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.service.controls.Control;
+
+import com.android.internal.R;
+import com.android.internal.util.Preconditions;
+
+/**
+ * A template for a {@link Control} that displays an image.
+ */
+public final class ThumbnailTemplate extends ControlTemplate {
+
+ private static final @TemplateType int TYPE = TYPE_THUMBNAIL;
+ private static final String KEY_ICON = "key_icon";
+ private static final String KEY_ACTIVE = "key_active";
+ private static final String KEY_CONTENT_DESCRIPTION = "key_content_description";
+
+ private final boolean mActive;
+ private final @NonNull Icon mThumbnail;
+ private final @NonNull CharSequence mContentDescription;
+
+ /**
+ * @param templateId the identifier for this template object
+ * @param active whether the image corresponds to an active (live) stream.
+ * @param thumbnail an image to display on the {@link Control}
+ * @param contentDescription a description of the image for accessibility.
+ */
+ public ThumbnailTemplate(
+ @NonNull String templateId,
+ boolean active,
+ @NonNull Icon thumbnail,
+ @NonNull CharSequence contentDescription) {
+ super(templateId);
+ Preconditions.checkNotNull(thumbnail);
+ Preconditions.checkNotNull(contentDescription);
+ mActive = active;
+ mThumbnail = thumbnail;
+ mContentDescription = contentDescription;
+ }
+
+ /**
+ * @param b
+ * @hide
+ */
+ ThumbnailTemplate(Bundle b) {
+ super(b);
+ mActive = b.getBoolean(KEY_ACTIVE);
+ mThumbnail = b.getParcelable(KEY_ICON);
+ mContentDescription = b.getCharSequence(KEY_CONTENT_DESCRIPTION, "");
+ }
+
+ /*
+ * @return {@code true} if the thumbnail corresponds to an active (live) stream.
+ */
+ public boolean isActive() {
+ return mActive;
+ }
+
+ /**
+ * The {@link Icon} (image) displayed by this template.
+ */
+ @NonNull
+ public Icon getThumbnail() {
+ return mThumbnail;
+ }
+
+ /**
+ * The description of the image returned by {@link ThumbnailTemplate#getThumbnail()}
+ */
+ @NonNull
+ public CharSequence getContentDescription() {
+ return mContentDescription;
+ }
+
+ /**
+ * @return {@link ControlTemplate#TYPE_THUMBNAIL}
+ */
+ @Override
+ public int getTemplateType() {
+ return TYPE;
+ }
+
+ /**
+ * Rescales the image down if necessary (in the case of a Bitmap).
+ *
+ * @hide
+ */
+ @Override
+ public void prepareTemplateForBinder(@NonNull Context context) {
+ int width = context.getResources()
+ .getDimensionPixelSize(R.dimen.controls_thumbnail_image_max_width);
+ int height = context.getResources()
+ .getDimensionPixelSize(R.dimen.controls_thumbnail_image_max_height);
+ rescaleThumbnail(width, height);
+ }
+
+ private void rescaleThumbnail(int width, int height) {
+ mThumbnail.scaleDownIfNecessary(width, height);
+ }
+
+ /**
+ * @return
+ * @hide
+ */
+ @Override
+ @NonNull
+ Bundle getDataBundle() {
+ Bundle b = super.getDataBundle();
+ b.putBoolean(KEY_ACTIVE, mActive);
+ b.putObject(KEY_ICON, mThumbnail);
+ b.putObject(KEY_CONTENT_DESCRIPTION, mContentDescription);
+ return b;
+ }
+}
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 08d9905..579a8bf 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -16,7 +16,7 @@
package android.service.notification;
-import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
+import static android.text.TextUtils.formatSimple;
import android.annotation.NonNull;
import android.app.Notification;
@@ -31,8 +31,6 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.nano.MetricsProto;
@@ -258,7 +256,7 @@
@Override
public String toString() {
- return String.format(
+ return formatSimple(
"StatusBarNotification(pkg=%s user=%s id=%d tag=%s key=%s: %s)",
this.pkg, this.user, this.id, this.tag,
this.key, this.notification);
diff --git a/core/java/android/text/Editable.java b/core/java/android/text/Editable.java
index 3396bce..a942f6c 100644
--- a/core/java/android/text/Editable.java
+++ b/core/java/android/text/Editable.java
@@ -137,7 +137,7 @@
}
/**
- * Returns a new SpannedStringBuilder from the specified
+ * Returns a new SpannableStringBuilder from the specified
* CharSequence. You can override this to provide
* a different kind of Spanned.
*/
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index 4a0bec1..c2e3a80 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -29,6 +29,7 @@
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.SpannedString;
+import android.text.TextUtils;
import java.text.SimpleDateFormat;
import java.util.Calendar;
@@ -697,7 +698,7 @@
}
private static String zeroPad(int inValue, int inMinDigits) {
- return String.format(Locale.getDefault(), "%0" + inMinDigits + "d", inValue);
+ return TextUtils.formatSimple("%0" + inMinDigits + "d", inValue);
}
/**
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index ee98b65..a7d20b5 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -64,7 +64,7 @@
private Exception mLastWtf;
// Layout of event log entry received from Android logger.
- // see system/core/liblog/include/log/log_read.h
+ // see system/logging/liblog/include/log/log_read.h
private static final int LENGTH_OFFSET = 0;
private static final int HEADER_SIZE_OFFSET = 2;
private static final int PROCESS_OFFSET = 4;
diff --git a/core/java/android/util/LocalLog.java b/core/java/android/util/LocalLog.java
index fda5e0d..bf9a838 100644
--- a/core/java/android/util/LocalLog.java
+++ b/core/java/android/util/LocalLog.java
@@ -60,10 +60,9 @@
}
final String logLine;
if (mUseLocalTimestamps) {
- logLine = String.format("%s - %s", LocalDateTime.now(), msg);
+ logLine = LocalDateTime.now() + " - " + msg;
} else {
- logLine = String.format(
- "%s / %s - %s", SystemClock.elapsedRealtime(), Instant.now(), msg);
+ logLine = SystemClock.elapsedRealtime() + " / " + Instant.now() + " - " + msg;
}
append(logLine);
}
diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java
index 35af0f2..9d8f7c3 100644
--- a/core/java/android/view/DragEvent.java
+++ b/core/java/android/view/DragEvent.java
@@ -140,6 +140,21 @@
boolean mDragResult;
boolean mEventHandlerWasCalled;
+ /**
+ * The drag surface containing the object being dragged. Only provided if the target window
+ * has the {@link WindowManager.LayoutParams#PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP} flag
+ * and is only sent with {@link #ACTION_DROP}.
+ */
+ private SurfaceControl mDragSurface;
+
+ /**
+ * The offsets from the touch that the surface is adjusted by as the surface is moved around the
+ * screen. Necessary for the target using the drag surface to animate it properly once it takes
+ * ownership of the drag surface after the drop.
+ */
+ private float mOffsetX;
+ private float mOffsetY;
+
private DragEvent mNext;
private RuntimeException mRecycledLocation;
private boolean mRecycled;
@@ -274,32 +289,37 @@
private DragEvent() {
}
- private void init(int action, float x, float y, ClipDescription description, ClipData data,
+ private void init(int action, float x, float y, float offsetX, float offsetY,
+ ClipDescription description, ClipData data, SurfaceControl dragSurface,
IDragAndDropPermissions dragAndDropPermissions, Object localState, boolean result) {
mAction = action;
mX = x;
mY = y;
+ mOffsetX = offsetX;
+ mOffsetY = offsetY;
mClipDescription = description;
mClipData = data;
- this.mDragAndDropPermissions = dragAndDropPermissions;
+ mDragSurface = dragSurface;
+ mDragAndDropPermissions = dragAndDropPermissions;
mLocalState = localState;
mDragResult = result;
}
static DragEvent obtain() {
- return DragEvent.obtain(0, 0f, 0f, null, null, null, null, false);
+ return DragEvent.obtain(0, 0f, 0f, 0f, 0f, null, null, null, null, null, false);
}
/** @hide */
- public static DragEvent obtain(int action, float x, float y, Object localState,
- ClipDescription description, ClipData data,
- IDragAndDropPermissions dragAndDropPermissions, boolean result) {
+ public static DragEvent obtain(int action, float x, float y, float offsetX, float offsetY,
+ Object localState, ClipDescription description, ClipData data,
+ SurfaceControl dragSurface, IDragAndDropPermissions dragAndDropPermissions,
+ boolean result) {
final DragEvent ev;
synchronized (gRecyclerLock) {
if (gRecyclerTop == null) {
ev = new DragEvent();
- ev.init(action, x, y, description, data, dragAndDropPermissions, localState,
- result);
+ ev.init(action, x, y, offsetX, offsetY, description, data, dragSurface,
+ dragAndDropPermissions, localState, result);
return ev;
}
ev = gRecyclerTop;
@@ -310,7 +330,8 @@
ev.mRecycled = false;
ev.mNext = null;
- ev.init(action, x, y, description, data, dragAndDropPermissions, localState, result);
+ ev.init(action, x, y, offsetX, offsetY, description, data, dragSurface,
+ dragAndDropPermissions, localState, result);
return ev;
}
@@ -318,9 +339,9 @@
/** @hide */
@UnsupportedAppUsage
public static DragEvent obtain(DragEvent source) {
- return obtain(source.mAction, source.mX, source.mY, source.mLocalState,
- source.mClipDescription, source.mClipData, source.mDragAndDropPermissions,
- source.mDragResult);
+ return obtain(source.mAction, source.mX, source.mY, source.mOffsetX, source.mOffsetY,
+ source.mLocalState, source.mClipDescription, source.mClipData, source.mDragSurface,
+ source.mDragAndDropPermissions, source.mDragResult);
}
/**
@@ -358,6 +379,16 @@
return mY;
}
+ /** @hide */
+ public float getOffsetX() {
+ return mOffsetX;
+ }
+
+ /** @hide */
+ public float getOffsetY() {
+ return mOffsetY;
+ }
+
/**
* Returns the {@link android.content.ClipData} object sent to the system as part of the call
* to
@@ -387,6 +418,11 @@
}
/** @hide */
+ public SurfaceControl getDragSurface() {
+ return mDragSurface;
+ }
+
+ /** @hide */
public IDragAndDropPermissions getDragAndDropPermissions() {
return mDragAndDropPermissions;
}
@@ -472,6 +508,34 @@
}
/**
+ * Returns a string that represents the symbolic name of the specified unmasked action
+ * such as "ACTION_DRAG_START", "ACTION_DRAG_END" or an equivalent numeric constant
+ * such as "35" if unknown.
+ *
+ * @param action The action.
+ * @return The symbolic name of the specified action.
+ * @see #getAction()
+ * @hide
+ */
+ public static String actionToString(int action) {
+ switch (action) {
+ case ACTION_DRAG_STARTED:
+ return "ACTION_DRAG_STARTED";
+ case ACTION_DRAG_LOCATION:
+ return "ACTION_DRAG_LOCATION";
+ case ACTION_DROP:
+ return "ACTION_DROP";
+ case ACTION_DRAG_ENDED:
+ return "ACTION_DRAG_ENDED";
+ case ACTION_DRAG_ENTERED:
+ return "ACTION_DRAG_ENTERED";
+ case ACTION_DRAG_EXITED:
+ return "ACTION_DRAG_EXITED";
+ }
+ return Integer.toString(action);
+ }
+
+ /**
* Returns a string containing a concise, human-readable representation of this DragEvent
* object.
* @return A string representation of the DragEvent object.
@@ -504,6 +568,8 @@
dest.writeInt(mAction);
dest.writeFloat(mX);
dest.writeFloat(mY);
+ dest.writeFloat(mOffsetX);
+ dest.writeFloat(mOffsetY);
dest.writeInt(mDragResult ? 1 : 0);
if (mClipData == null) {
dest.writeInt(0);
@@ -517,6 +583,12 @@
dest.writeInt(1);
mClipDescription.writeToParcel(dest, flags);
}
+ if (mDragSurface == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ mDragSurface.writeToParcel(dest, flags);
+ }
if (mDragAndDropPermissions == null) {
dest.writeInt(0);
} else {
@@ -535,6 +607,8 @@
event.mAction = in.readInt();
event.mX = in.readFloat();
event.mY = in.readFloat();
+ event.mOffsetX = in.readFloat();
+ event.mOffsetY = in.readFloat();
event.mDragResult = (in.readInt() != 0);
if (in.readInt() != 0) {
event.mClipData = ClipData.CREATOR.createFromParcel(in);
@@ -543,6 +617,9 @@
event.mClipDescription = ClipDescription.CREATOR.createFromParcel(in);
}
if (in.readInt() != 0) {
+ event.mDragSurface = SurfaceControl.CREATOR.createFromParcel(in);
+ }
+ if (in.readInt() != 0) {
event.mDragAndDropPermissions =
IDragAndDropPermissions.Stub.asInterface(in.readStrongBinder());;
}
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index 983ab2e..f209d88 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -19,6 +19,7 @@
import android.app.ActivityManager;
import android.view.IRemoteAnimationFinishedCallback;
import android.graphics.GraphicBuffer;
+import android.graphics.Rect;
/**
* Passed to the {@link IRecentsAnimationRunner} in order for the runner to control to let the
@@ -37,6 +38,15 @@
ActivityManager.TaskSnapshot screenshotTask(int taskId);
/**
+ * Sets the final bounds on a Task. This is used by Launcher to notify the system that
+ * animating Activity to PiP has completed and the associated task surface should be updated
+ * accordingly. This should be called before `finish`
+ * @param taskId for which the leash should be updated
+ * @param destinationBounds bounds of the final PiP window
+ */
+ void setFinishTaskBounds(int taskId, in Rect destinationBounds);
+
+ /**
* Notifies to the system that the animation into Recents should end, and all leashes associated
* with remote animation targets should be relinquished. If {@param moveHomeToTop} is true, then
* the home activity should be moved to the top. Otherwise, the home activity is hidden and the
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index f572eb9..d226f60 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -140,7 +140,6 @@
* @displayId The ID of the display where this token should be removed.
*/
void removeWindowToken(IBinder token, int displayId);
- void prepareAppTransition(int transit, boolean alwaysKeepCurrent);
/**
* Sets a singular remote controller of display rotations. There can only be one. The
@@ -180,8 +179,6 @@
@UnsupportedAppUsage
void overridePendingAppTransitionRemote(in RemoteAnimationAdapter remoteAnimationAdapter,
int displayId);
- @UnsupportedAppUsage
- void executeAppTransition();
/**
* Used by system ui to report that recents has shown itself.
@@ -758,6 +755,8 @@
/**
* Holds the WM lock for the specified amount of milliseconds.
* Intended for use by the tests that need to imitate lock contention.
+ * The token should be obtained by
+ * {@link android.content.pm.PackageManager#getHoldLockToken()}.
*/
- void holdLock(in int durationMs);
+ void holdLock(in IBinder token, in int durationMs);
}
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 355b314..7261765 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -16,11 +16,11 @@
package android.view;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Rect;
@@ -43,25 +43,16 @@
public class NotificationHeaderView extends ViewGroup {
private final int mChildMinWidth;
private final int mContentEndMargin;
- private final int mGravity;
- private View mAppName;
- private View mHeaderText;
- private View mSecondaryHeaderText;
private OnClickListener mExpandClickListener;
- private OnClickListener mFeedbackListener;
private HeaderTouchListener mTouchListener = new HeaderTouchListener();
+ private NotificationTopLineView mTopLineView;
private NotificationExpandButton mExpandButton;
private CachingIconView mIcon;
- private View mProfileBadge;
- private View mFeedbackIcon;
- private boolean mShowExpandButtonAtEnd;
- private boolean mShowWorkBadgeAtEnd;
private int mHeaderTextMarginEnd;
private Drawable mBackground;
private boolean mEntireHeaderClickable;
private boolean mExpandOnlyOnButton;
private boolean mAcceptAllTouches;
- private int mTotalWidth;
ViewOutlineProvider mProvider = new ViewOutlineProvider() {
@Override
@@ -86,29 +77,22 @@
this(context, attrs, defStyleAttr, 0);
}
- public NotificationHeaderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ public NotificationHeaderView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
Resources res = getResources();
mChildMinWidth = res.getDimensionPixelSize(R.dimen.notification_header_shrink_min_width);
mContentEndMargin = res.getDimensionPixelSize(R.dimen.notification_content_margin_end);
mEntireHeaderClickable = res.getBoolean(R.bool.config_notificationHeaderClickableForExpand);
-
- int[] attrIds = { android.R.attr.gravity };
- TypedArray ta = context.obtainStyledAttributes(attrs, attrIds, defStyleAttr, defStyleRes);
- mGravity = ta.getInt(0, 0);
- ta.recycle();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mAppName = findViewById(com.android.internal.R.id.app_name_text);
- mHeaderText = findViewById(com.android.internal.R.id.header_text);
- mSecondaryHeaderText = findViewById(com.android.internal.R.id.header_text_secondary);
- mExpandButton = findViewById(com.android.internal.R.id.expand_button);
- mIcon = findViewById(com.android.internal.R.id.icon);
- mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
- mFeedbackIcon = findViewById(com.android.internal.R.id.feedback);
+ mIcon = findViewById(R.id.icon);
+ mTopLineView = findViewById(R.id.notification_top_line);
+ mExpandButton = findViewById(R.id.expand_button);
+ setClipToPadding(false);
}
@Override
@@ -134,9 +118,7 @@
lp.topMargin + lp.bottomMargin, lp.height);
child.measure(childWidthSpec, childHeightSpec);
// Icons that should go at the end
- if ((child == mExpandButton && mShowExpandButtonAtEnd)
- || child == mProfileBadge
- || child == mFeedbackIcon) {
+ if (child == mExpandButton) {
iconWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
} else {
totalWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
@@ -147,19 +129,10 @@
int endMargin = Math.max(mHeaderTextMarginEnd, iconWidth);
if (totalWidth > givenWidth - endMargin) {
int overFlow = totalWidth - givenWidth + endMargin;
- // We are overflowing, lets shrink the app name first
- overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mAppName,
+ // We are overflowing; shrink the top line
+ shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mTopLineView,
mChildMinWidth);
-
- // still overflowing, we shrink the header text
- overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mHeaderText, 0);
-
- // still overflowing, finally we shrink the secondary header text
- shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mSecondaryHeaderText,
- 0);
}
- totalWidth += getPaddingEnd();
- mTotalWidth = Math.min(totalWidth, givenWidth);
setMeasuredDimension(givenWidth, givenHeight);
}
@@ -180,10 +153,6 @@
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left = getPaddingStart();
int end = getMeasuredWidth();
- final boolean centerAligned = (mGravity & Gravity.CENTER_HORIZONTAL) != 0;
- if (centerAligned) {
- left += getMeasuredWidth() / 2 - mTotalWidth / 2;
- }
int childCount = getChildCount();
int ownHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
for (int i = 0; i < childCount; i++) {
@@ -198,9 +167,7 @@
int top = (int) (getPaddingTop() + (ownHeight - childHeight) / 2.0f);
int bottom = top + childHeight;
// Icons that should go at the end
- if ((child == mExpandButton && mShowExpandButtonAtEnd)
- || child == mProfileBadge
- || child == mFeedbackIcon) {
+ if (child == mExpandButton) {
if (end == getMeasuredWidth()) {
layoutRight = end - mContentEndMargin;
} else {
@@ -227,7 +194,7 @@
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new ViewGroup.MarginLayoutParams(getContext(), attrs);
+ return new MarginLayoutParams(getContext(), attrs);
}
/**
@@ -256,7 +223,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
return super.verifyDrawable(who) || who == mBackground;
}
@@ -268,7 +235,7 @@
}
private void updateTouchListener() {
- if (mExpandClickListener == null && mFeedbackListener == null) {
+ if (mExpandClickListener == null) {
setOnTouchListener(null);
return;
}
@@ -276,15 +243,6 @@
mTouchListener.bindTouchRects();
}
- /**
- * Sets onclick listener for feedback icon.
- */
- public void setFeedbackOnClickListener(OnClickListener l) {
- mFeedbackListener = l;
- mFeedbackIcon.setOnClickListener(mFeedbackListener);
- updateTouchListener();
- }
-
@Override
public void setOnClickListener(@Nullable OnClickListener l) {
mExpandClickListener = l;
@@ -292,25 +250,6 @@
updateTouchListener();
}
- public void setShowWorkBadgeAtEnd(boolean showWorkBadgeAtEnd) {
- if (showWorkBadgeAtEnd != mShowWorkBadgeAtEnd) {
- setClipToPadding(!showWorkBadgeAtEnd);
- mShowWorkBadgeAtEnd = showWorkBadgeAtEnd;
- }
- }
-
- /**
- * Sets whether or not the expand button appears at the end of the NotificationHeaderView. If
- * both this and {@link #setShowWorkBadgeAtEnd(boolean)} have been set to true, then the
- * expand button will appear closer to the end than the work badge.
- */
- public void setShowExpandButtonAtEnd(boolean showExpandButtonAtEnd) {
- if (showExpandButtonAtEnd != mShowExpandButtonAtEnd) {
- setClipToPadding(!showExpandButtonAtEnd);
- mShowExpandButtonAtEnd = showExpandButtonAtEnd;
- }
- }
-
/**
* Sets the margin end for the text portion of the header, excluding right-aligned elements
* @param headerTextMarginEnd margin size
@@ -331,11 +270,13 @@
return mHeaderTextMarginEnd;
}
- public class HeaderTouchListener implements View.OnTouchListener {
+ /**
+ * Handles clicks on the header based on the region tapped.
+ */
+ public class HeaderTouchListener implements OnTouchListener {
private final ArrayList<Rect> mTouchRects = new ArrayList<>();
private Rect mExpandButtonRect;
- private Rect mFeedbackRect;
private int mTouchSlop;
private boolean mTrackGesture;
private float mDownX;
@@ -348,7 +289,6 @@
mTouchRects.clear();
addRectAroundView(mIcon);
mExpandButtonRect = addRectAroundView(mExpandButton);
- mFeedbackRect = addRectAroundView(mFeedbackIcon);
addWidthRect();
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
@@ -409,11 +349,11 @@
break;
case MotionEvent.ACTION_UP:
if (mTrackGesture) {
- if (mFeedbackIcon.isVisibleToUser()
- && (mFeedbackRect.contains((int) x, (int) y)
- || mFeedbackRect.contains((int) mDownX, (int) mDownY))) {
- mFeedbackIcon.performClick();
- return true;
+ float topLineX = mTopLineView.getX();
+ float topLineY = mTopLineView.getY();
+ if (mTopLineView.onTouchUp(x - topLineX, y - topLineY,
+ mDownX - topLineX, mDownY - topLineY)) {
+ break;
}
mExpandButton.performClick();
}
@@ -435,7 +375,9 @@
return true;
}
}
- return false;
+ float topLineX = x - mTopLineView.getX();
+ float topLineY = y - mTopLineView.getY();
+ return mTopLineView.isInTouchRect(topLineX, topLineY);
}
}
diff --git a/core/java/android/view/NotificationTopLineView.java b/core/java/android/view/NotificationTopLineView.java
new file mode 100644
index 0000000..9443ccf
--- /dev/null
+++ b/core/java/android/view/NotificationTopLineView.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.widget.RemoteViews;
+
+import com.android.internal.R;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * The top line of content in a notification view.
+ * This includes the text views and badges but excludes the icon and the expander.
+ *
+ * @hide
+ */
+@RemoteViews.RemoteView
+public class NotificationTopLineView extends ViewGroup {
+ private final int mChildMinWidth;
+ private final int mContentEndMargin;
+ private View mAppName;
+ private View mHeaderText;
+ private View mSecondaryHeaderText;
+ private OnClickListener mFeedbackListener;
+ private HeaderTouchListener mTouchListener = new HeaderTouchListener();
+ private View mProfileBadge;
+ private View mFeedbackIcon;
+ private int mHeaderTextMarginEnd;
+ private List<View> mIconsAtEnd;
+
+ public NotificationTopLineView(Context context) {
+ this(context, null);
+ }
+
+ public NotificationTopLineView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public NotificationTopLineView(Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public NotificationTopLineView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ Resources res = getResources();
+ mChildMinWidth = res.getDimensionPixelSize(R.dimen.notification_header_shrink_min_width);
+ mContentEndMargin = res.getDimensionPixelSize(R.dimen.notification_content_margin_end);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mAppName = findViewById(R.id.app_name_text);
+ mHeaderText = findViewById(R.id.header_text);
+ mSecondaryHeaderText = findViewById(R.id.header_text_secondary);
+ mProfileBadge = findViewById(R.id.profile_badge);
+ mFeedbackIcon = findViewById(R.id.feedback);
+ mIconsAtEnd = Arrays.asList(mProfileBadge, mFeedbackIcon);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int givenWidth = MeasureSpec.getSize(widthMeasureSpec);
+ final int givenHeight = MeasureSpec.getSize(heightMeasureSpec);
+ int wrapContentWidthSpec = MeasureSpec.makeMeasureSpec(givenWidth,
+ MeasureSpec.AT_MOST);
+ int wrapContentHeightSpec = MeasureSpec.makeMeasureSpec(givenHeight,
+ MeasureSpec.AT_MOST);
+ int totalWidth = getPaddingStart();
+ int iconWidth = getPaddingEnd();
+ for (int i = 0; i < getChildCount(); i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == GONE) {
+ // We'll give it the rest of the space in the end
+ continue;
+ }
+ final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
+ int childWidthSpec = getChildMeasureSpec(wrapContentWidthSpec,
+ lp.leftMargin + lp.rightMargin, lp.width);
+ int childHeightSpec = getChildMeasureSpec(wrapContentHeightSpec,
+ lp.topMargin + lp.bottomMargin, lp.height);
+ child.measure(childWidthSpec, childHeightSpec);
+ // Icons that should go at the end
+ if (mIconsAtEnd.contains(child)) {
+ iconWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
+ } else {
+ totalWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
+ }
+ }
+
+ // Ensure that there is at least enough space for the icons
+ int endMargin = Math.max(mHeaderTextMarginEnd, iconWidth);
+ if (totalWidth > givenWidth - endMargin) {
+ int overFlow = totalWidth - givenWidth + endMargin;
+ // We are overflowing, lets shrink the app name first
+ overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mAppName,
+ mChildMinWidth);
+
+ // still overflowing, we shrink the header text
+ overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mHeaderText, 0);
+
+ // still overflowing, finally we shrink the secondary header text
+ shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mSecondaryHeaderText,
+ 0);
+ }
+ setMeasuredDimension(givenWidth, givenHeight);
+ }
+
+ private int shrinkViewForOverflow(int heightSpec, int overFlow, View targetView,
+ int minimumWidth) {
+ final int oldWidth = targetView.getMeasuredWidth();
+ if (overFlow > 0 && targetView.getVisibility() != GONE && oldWidth > minimumWidth) {
+ // we're still too big
+ int newSize = Math.max(minimumWidth, oldWidth - overFlow);
+ int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
+ targetView.measure(childWidthSpec, heightSpec);
+ overFlow -= oldWidth - newSize;
+ }
+ return overFlow;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int left = getPaddingStart();
+ int end = getMeasuredWidth();
+ int childCount = getChildCount();
+ int ownHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() == GONE) {
+ continue;
+ }
+ int childHeight = child.getMeasuredHeight();
+ MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
+ int layoutLeft;
+ int layoutRight;
+ int top = (int) (getPaddingTop() + (ownHeight - childHeight) / 2.0f);
+ int bottom = top + childHeight;
+ // Icons that should go at the end
+ if (mIconsAtEnd.contains(child)) {
+ if (end == getMeasuredWidth()) {
+ layoutRight = end - mContentEndMargin;
+ } else {
+ layoutRight = end - params.getMarginEnd();
+ }
+ layoutLeft = layoutRight - child.getMeasuredWidth();
+ end = layoutLeft - params.getMarginStart();
+ } else {
+ left += params.getMarginStart();
+ int right = left + child.getMeasuredWidth();
+ layoutLeft = left;
+ layoutRight = right;
+ left = right + params.getMarginEnd();
+ }
+ if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
+ int ltrLeft = layoutLeft;
+ layoutLeft = getWidth() - layoutRight;
+ layoutRight = getWidth() - ltrLeft;
+ }
+ child.layout(layoutLeft, top, layoutRight, bottom);
+ }
+ updateTouchListener();
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new MarginLayoutParams(getContext(), attrs);
+ }
+
+ private void updateTouchListener() {
+ if (mFeedbackListener == null) {
+ setOnTouchListener(null);
+ return;
+ }
+ setOnTouchListener(mTouchListener);
+ mTouchListener.bindTouchRects();
+ }
+
+ /**
+ * Sets onclick listener for feedback icon.
+ */
+ public void setFeedbackOnClickListener(OnClickListener l) {
+ mFeedbackListener = l;
+ mFeedbackIcon.setOnClickListener(mFeedbackListener);
+ updateTouchListener();
+ }
+
+ /**
+ * Sets the margin end for the text portion of the header, excluding right-aligned elements
+ *
+ * @param headerTextMarginEnd margin size
+ */
+ public void setHeaderTextMarginEnd(int headerTextMarginEnd) {
+ if (mHeaderTextMarginEnd != headerTextMarginEnd) {
+ mHeaderTextMarginEnd = headerTextMarginEnd;
+ requestLayout();
+ }
+ }
+
+ /**
+ * Get the current margin end value for the header text
+ *
+ * @return margin size
+ */
+ public int getHeaderTextMarginEnd() {
+ return mHeaderTextMarginEnd;
+ }
+
+ private class HeaderTouchListener implements OnTouchListener {
+
+ private Rect mFeedbackRect;
+ private int mTouchSlop;
+ private boolean mTrackGesture;
+ private float mDownX;
+ private float mDownY;
+
+ HeaderTouchListener() {
+ }
+
+ public void bindTouchRects() {
+ mFeedbackRect = getRectAroundView(mFeedbackIcon);
+ mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+ }
+
+ private Rect getRectAroundView(View view) {
+ float size = 48 * getResources().getDisplayMetrics().density;
+ float width = Math.max(size, view.getWidth());
+ float height = Math.max(size, view.getHeight());
+ final Rect r = new Rect();
+ if (view.getVisibility() == GONE) {
+ view = getFirstChildNotGone();
+ r.left = (int) (view.getLeft() - width / 2.0f);
+ } else {
+ r.left = (int) ((view.getLeft() + view.getRight()) / 2.0f - width / 2.0f);
+ }
+ r.top = (int) ((view.getTop() + view.getBottom()) / 2.0f - height / 2.0f);
+ r.bottom = (int) (r.top + height);
+ r.right = (int) (r.left + width);
+ return r;
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ float x = event.getX();
+ float y = event.getY();
+ switch (event.getActionMasked() & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN:
+ mTrackGesture = false;
+ if (isInside(x, y)) {
+ mDownX = x;
+ mDownY = y;
+ mTrackGesture = true;
+ return true;
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (mTrackGesture) {
+ if (Math.abs(mDownX - x) > mTouchSlop
+ || Math.abs(mDownY - y) > mTouchSlop) {
+ mTrackGesture = false;
+ }
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (mTrackGesture && onTouchUp(x, y, mDownX, mDownY)) {
+ return true;
+ }
+ break;
+ }
+ return mTrackGesture;
+ }
+
+ private boolean onTouchUp(float upX, float upY, float downX, float downY) {
+ if (mFeedbackIcon.isVisibleToUser()
+ && (mFeedbackRect.contains((int) upX, (int) upY)
+ || mFeedbackRect.contains((int) downX, (int) downY))) {
+ mFeedbackIcon.performClick();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isInside(float x, float y) {
+ return mFeedbackRect.contains((int) x, (int) y);
+ }
+ }
+
+ private View getFirstChildNotGone() {
+ for (int i = 0; i < getChildCount(); i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ return child;
+ }
+ }
+ return this;
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
+ /**
+ * Determine if the given point is touching an active part of the top line.
+ */
+ public boolean isInTouchRect(float x, float y) {
+ if (mFeedbackListener == null) {
+ return false;
+ }
+ return mTouchListener.isInside(x, y);
+ }
+
+ /**
+ * Perform a click on an active part of the top line, if touching.
+ */
+ public boolean onTouchUp(float upX, float upY, float downX, float downY) {
+ if (mFeedbackListener == null) {
+ return false;
+ }
+ return mTouchListener.onTouchUp(upX, upY, downX, downY);
+ }
+}
diff --git a/core/java/android/view/OnReceiveContentCallback.java b/core/java/android/view/OnReceiveContentCallback.java
index a217ff6..d74938c 100644
--- a/core/java/android/view/OnReceiveContentCallback.java
+++ b/core/java/android/view/OnReceiveContentCallback.java
@@ -19,9 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.content.ClipData;
-import android.content.ClipDescription;
import android.net.Uri;
import android.os.Bundle;
@@ -30,11 +28,10 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
-import java.util.Set;
/**
- * Callback for apps to implement handling for insertion of content. "Content" here refers to both
- * text and non-text (plain/styled text, HTML, images, videos, audio files, etc).
+ * Callback for apps to implement handling for insertion of content. Content may be both text and
+ * non-text (plain/styled text, HTML, images, videos, audio files, etc).
*
* <p>This callback can be attached to different types of UI components using
* {@link View#setOnReceiveContentCallback}.
@@ -45,32 +42,38 @@
*
* <p>Example implementation:<br>
* <pre class="prettyprint">
+ * // (1) Define the callback
* public class MyOnReceiveContentCallback implements OnReceiveContentCallback<TextView> {
- *
- * private static final Set<String> SUPPORTED_MIME_TYPES = Collections.unmodifiableSet(
+ * public static final Set<String> MIME_TYPES = Collections.unmodifiableSet(
* Set.of("image/*", "video/*"));
*
- * @NonNull
- * @Override
- * public Set<String> getSupportedMimeTypes() {
- * return SUPPORTED_MIME_TYPES;
- * }
- *
* @Override
* public boolean onReceiveContent(@NonNull TextView view, @NonNull Payload payload) {
* // ... app-specific logic to handle the content in the payload ...
* }
* }
+ *
+ * // (2) Register the callback
+ * public class MyActivity extends Activity {
+ * @Override
+ * public void onCreate(Bundle savedInstanceState) {
+ * // ...
+ *
+ * EditText myInput = findViewById(R.id.my_input);
+ * myInput.setOnReceiveContentCallback(
+ * MyOnReceiveContentCallback.MIME_TYPES,
+ * new MyOnReceiveContentCallback());
+ * }
* </pre>
*
- * @param <T> The type of {@link View} with which this receiver can be associated.
+ * @param <T> The type of {@link View} with which this callback can be associated.
*/
public interface OnReceiveContentCallback<T extends View> {
/**
* Receive the given content.
*
- * <p>This function will only be invoked if the MIME type of the content is in the set of
- * types returned by {@link #getSupportedMimeTypes}.
+ * <p>This method is only invoked for content whose MIME type matches a type specified via
+ * {@link View#setOnReceiveContentCallback}.
*
* <p>For text, if the view has a selection, the selection should be overwritten by the clip; if
* there's no selection, this method should insert the content at the current cursor position.
@@ -81,54 +84,14 @@
* @param view The view where the content insertion was requested.
* @param payload The content to insert and related metadata.
*
- * @return Returns true if some or all of the content is accepted for insertion. If accepted,
- * actual insertion may be handled asynchronously in the background and may or may not result in
- * successful insertion. For example, the app may not end up inserting an accepted item if it
+ * @return Returns true if the content was handled in some way, false otherwise. Actual
+ * insertion may be processed asynchronously in the background and may or may not succeed even
+ * if this method returns true. For example, an app may not end up inserting an item if it
* exceeds the app's size limit for that type of content.
*/
boolean onReceiveContent(@NonNull T view, @NonNull Payload payload);
/**
- * Returns the MIME types that can be handled by this callback.
- *
- * <p>The {@link #onReceiveContent} callback method will only be invoked if the MIME type of the
- * content is in the set of supported types returned here.
- *
- * <p>Different platform features (e.g. pasting from the clipboard, inserting stickers from the
- * keyboard, etc) may use this function to conditionally alter their behavior. For example, the
- * keyboard may choose to hide its UI for inserting GIFs if the input field that has focus has
- * a {@link OnReceiveContentCallback} set and the MIME types returned from this function don't
- * include "image/gif".
- *
- * <p><em>Note: MIME type matching in the Android framework is case-sensitive, unlike formal RFC
- * MIME types. As a result, you should always write your MIME types with lower case letters, or
- * use {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to lower
- * case.</em>
- *
- * @param view The target view.
- * @return An immutable set with the MIME types supported by this callback. The returned MIME
- * types may contain wildcards such as "text/*", "image/*", etc.
- */
- @SuppressLint("CallbackMethodName")
- @NonNull
- Set<String> getSupportedMimeTypes(@NonNull T view);
-
- /**
- * Returns true if at least one of the MIME types of the given clip is
- * {@link #getSupportedMimeTypes supported} by this receiver.
- *
- * @hide
- */
- default boolean supports(@NonNull T view, @NonNull ClipDescription description) {
- for (String supportedMimeType : getSupportedMimeTypes(view)) {
- if (description.hasMimeType(supportedMimeType)) {
- return true;
- }
- }
- return false;
- }
-
- /**
* Holds all the relevant data for a request to {@link OnReceiveContentCallback}.
*/
final class Payload {
diff --git a/core/java/android/view/RemoteAnimationDefinition.java b/core/java/android/view/RemoteAnimationDefinition.java
index 5a8ac54..a5ff19e 100644
--- a/core/java/android/view/RemoteAnimationDefinition.java
+++ b/core/java/android/view/RemoteAnimationDefinition.java
@@ -19,7 +19,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import android.annotation.Nullable;
-import android.app.WindowConfiguration;
import android.app.WindowConfiguration.ActivityType;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.IBinder;
@@ -29,7 +28,7 @@
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
-import android.view.WindowManager.TransitionType;
+import android.view.WindowManager.TransitionOldType;
/**
* Defines which animation types should be overridden by which remote animation.
@@ -48,13 +47,13 @@
/**
* Registers a remote animation for a specific transition.
*
- * @param transition The transition type. Must be one of WindowManager.TRANSIT_* values.
+ * @param transition The old transition type. Must be one of WindowManager.TRANSIT_OLD_* values.
* @param activityTypeFilter The remote animation only runs if an activity with type of this
* parameter is involved in the transition.
* @param adapter The adapter that described how to run the remote animation.
*/
@UnsupportedAppUsage
- public void addRemoteAnimation(@TransitionType int transition,
+ public void addRemoteAnimation(@TransitionOldType int transition,
@ActivityType int activityTypeFilter, RemoteAnimationAdapter adapter) {
mTransitionAnimationMap.put(transition,
new RemoteAnimationAdapterEntry(adapter, activityTypeFilter));
@@ -64,35 +63,37 @@
* Registers a remote animation for a specific transition without defining an activity type
* filter.
*
- * @param transition The transition type. Must be one of WindowManager.TRANSIT_* values.
+ * @param transition The old transition type. Must be one of WindowManager.TRANSIT_OLD_* values.
* @param adapter The adapter that described how to run the remote animation.
*/
@UnsupportedAppUsage
- public void addRemoteAnimation(@TransitionType int transition, RemoteAnimationAdapter adapter) {
+ public void addRemoteAnimation(@TransitionOldType int transition,
+ RemoteAnimationAdapter adapter) {
addRemoteAnimation(transition, ACTIVITY_TYPE_UNDEFINED, adapter);
}
/**
* Checks whether a remote animation for specific transition is defined.
*
- * @param transition The transition type. Must be one of WindowManager.TRANSIT_* values.
+ * @param transition The old transition type. Must be one of WindowManager.TRANSIT_OLD_* values.
* @param activityTypes The set of activity types of activities that are involved in the
* transition. Will be used for filtering.
* @return Whether this definition has defined a remote animation for the specified transition.
*/
- public boolean hasTransition(@TransitionType int transition, ArraySet<Integer> activityTypes) {
+ public boolean hasTransition(@TransitionOldType int transition,
+ ArraySet<Integer> activityTypes) {
return getAdapter(transition, activityTypes) != null;
}
/**
* Retrieves the remote animation for a specific transition.
*
- * @param transition The transition type. Must be one of WindowManager.TRANSIT_* values.
+ * @param transition The old transition type. Must be one of WindowManager.TRANSIT_OLD_* values.
* @param activityTypes The set of activity types of activities that are involved in the
* transition. Will be used for filtering.
* @return The remote animation adapter for the specified transition.
*/
- public @Nullable RemoteAnimationAdapter getAdapter(@TransitionType int transition,
+ public @Nullable RemoteAnimationAdapter getAdapter(@TransitionOldType int transition,
ArraySet<Integer> activityTypes) {
final RemoteAnimationAdapterEntry entry = mTransitionAnimationMap.get(transition);
if (entry == null) {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 432d927..14748f0 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1207,7 +1207,7 @@
// Therefore, we must explicitly recreate the {@link Surface} in these
// cases.
if (mUseBlastAdapter) {
- mSurface.transferFrom(mBlastBufferQueue.createSurface());
+ mSurface.transferFrom(mBlastBufferQueue.createSurfaceWithHandle());
} else {
mSurface.createFrom(mSurfaceControl);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 667f0b9..ac628e1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -47,6 +47,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AutofillOptions;
import android.content.ClipData;
+import android.content.ClipDescription;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
@@ -80,7 +81,6 @@
import android.hardware.display.DisplayManagerGlobal;
import android.net.Uri;
import android.os.Build;
-import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -144,6 +144,7 @@
import com.android.internal.R;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.Preconditions;
import com.android.internal.view.ScrollCaptureInternal;
import com.android.internal.view.TooltipPopup;
import com.android.internal.view.menu.MenuBuilder;
@@ -5244,7 +5245,9 @@
int mUnbufferedInputSource = InputDevice.SOURCE_CLASS_NONE;
@Nullable
- private OnReceiveContentCallback<? extends View> mOnReceiveContentCallback;
+ private String[] mOnReceiveContentMimeTypes;
+ @Nullable
+ private OnReceiveContentCallback mOnReceiveContentCallback;
/**
* Simple constructor to use when creating a view from code.
@@ -9002,36 +9005,78 @@
}
/**
- * Returns the callback used for handling insertion of content into this view. See
- * {@link #setOnReceiveContentCallback} for more info.
- *
- * @return The callback for handling insertion of content. Returns null if no callback has been
- * {@link #setOnReceiveContentCallback set}.
- */
- @Nullable
- public OnReceiveContentCallback<? extends View> getOnReceiveContentCallback() {
- return mOnReceiveContentCallback;
- }
-
- /**
* Sets the callback to handle insertion of content into this view.
*
* <p>Depending on the view, this callback may be invoked for scenarios such as content
* insertion from the IME, Autofill, etc.
*
- * <p>The callback will only be invoked if the MIME type of the content is
- * {@link OnReceiveContentCallback#getSupportedMimeTypes declared as supported} by the callback.
- * If the content type is not supported by the callback, the default platform handling will be
- * executed instead.
+ * <p>This callback is only invoked for content whose MIME type matches a type specified via
+ * the {code mimeTypes} parameter. If the MIME type is not supported by the callback, the
+ * default platform handling will be executed instead (no-op for the default {@link View}).
*
+ * <p><em>Note: MIME type matching in the Android framework is case-sensitive, unlike formal RFC
+ * MIME types. As a result, you should always write your MIME types with lower case letters, or
+ * use {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to lower
+ * case.</em>
+ *
+ * @param mimeTypes The type of content for which the callback should be invoked. This may use
+ * wildcards such as "text/*", "image/*", etc. This must not be null or empty if a non-null
+ * callback is passed in.
* @param callback The callback to use. This can be null to reset to the default behavior.
*/
- public void setOnReceiveContentCallback(
- @Nullable OnReceiveContentCallback<? extends View> callback) {
+ @SuppressWarnings("rawtypes")
+ public void setOnReceiveContentCallback(@Nullable String[] mimeTypes,
+ @Nullable OnReceiveContentCallback callback) {
+ if (callback != null) {
+ Preconditions.checkArgument(mimeTypes != null && mimeTypes.length > 0,
+ "When the callback is set, MIME types must also be set");
+ }
+ mOnReceiveContentMimeTypes = mimeTypes;
mOnReceiveContentCallback = callback;
}
/**
+ * Receives the given content. The default implementation invokes the callback set via
+ * {@link #setOnReceiveContentCallback}. If no callback is set or if the callback does not
+ * support the given content (based on the MIME type), returns false.
+ *
+ * @param payload The content to insert and related metadata.
+ *
+ * @return Returns true if the content was handled in some way, false otherwise. Actual
+ * insertion may be processed asynchronously in the background and may or may not succeed even
+ * if this method returns true. For example, an app may not end up inserting an item if it
+ * exceeds the app's size limit for that type of content.
+ */
+ public boolean onReceiveContent(@NonNull OnReceiveContentCallback.Payload payload) {
+ ClipDescription description = payload.getClip().getDescription();
+ if (mOnReceiveContentCallback != null && mOnReceiveContentMimeTypes != null
+ && description.hasMimeType(mOnReceiveContentMimeTypes)) {
+ return mOnReceiveContentCallback.onReceiveContent(this, payload);
+ }
+ return false;
+ }
+
+ /**
+ * Returns the MIME types that can be handled by {@link #onReceiveContent} for this view, as
+ * configured via {@link #setOnReceiveContentCallback}. By default returns null.
+ *
+ * <p>Different platform features (e.g. pasting from the clipboard, inserting stickers from the
+ * keyboard, etc) may use this function to conditionally alter their behavior. For example, the
+ * soft keyboard may choose to hide its UI for inserting GIFs for a particular input field if
+ * the MIME types returned here for that field don't include "image/gif".
+ *
+ * <p>Note: Comparisons of MIME types should be performed using utilities such as
+ * {@link ClipDescription#compareMimeTypes} rather than simple string equality, in order to
+ * correctly handle patterns (e.g. "text/*").
+ *
+ * @return The MIME types supported by {@link #onReceiveContent} for this view. The returned
+ * MIME types may contain wildcards such as "text/*", "image/*", etc.
+ */
+ public @Nullable String[] getOnReceiveContentMimeTypes() {
+ return mOnReceiveContentMimeTypes;
+ }
+
+ /**
* Automatically fills the content of this view with the {@code value}.
*
* <p>Views support the Autofill Framework mainly by:
@@ -26444,7 +26489,9 @@
surface.copyFrom(surfaceControl);
IBinder token = null;
try {
- final Canvas canvas = surface.lockCanvas(null);
+ final Canvas canvas = isHardwareAccelerated()
+ ? surface.lockHardwareCanvas()
+ : surface.lockCanvas(null);
try {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
shadowBuilder.onDrawShadow(canvas);
@@ -26535,7 +26582,9 @@
}
if (mAttachInfo.mDragToken != null) {
try {
- Canvas canvas = mAttachInfo.mDragSurface.lockCanvas(null);
+ Canvas canvas = isHardwareAccelerated()
+ ? mAttachInfo.mDragSurface.lockHardwareCanvas()
+ : mAttachInfo.mDragSurface.lockCanvas(null);
try {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
shadowBuilder.onDrawShadow(canvas);
@@ -28913,33 +28962,6 @@
boolean mUse32BitDrawingCache;
/**
- * For windows that are full-screen but using insets to layout inside
- * of the screen decorations, these are the current insets for the
- * content of the window.
- */
- @UnsupportedAppUsage(maxTargetSdk = VERSION_CODES.Q,
- publicAlternatives = "Use {@link WindowInsets#getInsets(int)}")
- final Rect mContentInsets = new Rect();
-
- /**
- * For windows that are full-screen but using insets to layout inside
- * of the screen decorations, these are the current insets for the
- * actual visible parts of the window.
- */
- @UnsupportedAppUsage(maxTargetSdk = VERSION_CODES.Q,
- publicAlternatives = "Use {@link WindowInsets#getInsets(int)}")
- final Rect mVisibleInsets = new Rect();
-
- /**
- * For windows that are full-screen but using insets to layout inside
- * of the screen decorations, these are the current insets for the
- * stable system windows.
- */
- @UnsupportedAppUsage(maxTargetSdk = VERSION_CODES.Q,
- publicAlternatives = "Use {@link WindowInsets#getInsets(int)}")
- final Rect mStableInsets = new Rect();
-
- /**
* Current caption insets to the display coordinate.
*/
final Rect mCaptionInsets = new Rect();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9bc0770..f1005e91 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1758,7 +1758,6 @@
destroySurface();
}
}
- scheduleConsumeBatchedInputImmediately();
}
@@ -8314,11 +8313,8 @@
@Override
public void onBatchedInputEventPending(int source) {
- // mStopped: There will be no more choreographer callbacks if we are stopped,
- // so we must consume all input immediately to prevent ANR
final boolean unbuffered = mUnbufferedInputDispatch
- || (source & mUnbufferedInputSource) != SOURCE_CLASS_NONE
- || mStopped;
+ || (source & mUnbufferedInputSource) != SOURCE_CLASS_NONE;
if (unbuffered) {
if (mConsumeBatchedInputScheduled) {
unscheduleConsumeBatchedInput();
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 9336872..d540059 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -69,6 +69,7 @@
import android.app.KeyguardManager;
import android.app.Presentation;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ClipData;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
@@ -135,168 +136,207 @@
* Not set up for a transition.
* @hide
*/
- int TRANSIT_UNSET = -1;
+ int TRANSIT_OLD_UNSET = -1;
/**
* No animation for transition.
* @hide
*/
- int TRANSIT_NONE = 0;
+ int TRANSIT_OLD_NONE = 0;
/**
* A window in a new activity is being opened on top of an existing one in the same task.
* @hide
*/
- int TRANSIT_ACTIVITY_OPEN = 6;
+ int TRANSIT_OLD_ACTIVITY_OPEN = 6;
/**
* The window in the top-most activity is being closed to reveal the previous activity in the
* same task.
* @hide
*/
- int TRANSIT_ACTIVITY_CLOSE = 7;
+ int TRANSIT_OLD_ACTIVITY_CLOSE = 7;
/**
* A window in a new task is being opened on top of an existing one in another activity's task.
* @hide
*/
- int TRANSIT_TASK_OPEN = 8;
+ int TRANSIT_OLD_TASK_OPEN = 8;
/**
* A window in the top-most activity is being closed to reveal the previous activity in a
* different task.
* @hide
*/
- int TRANSIT_TASK_CLOSE = 9;
+ int TRANSIT_OLD_TASK_CLOSE = 9;
/**
* A window in an existing task is being displayed on top of an existing one in another
* activity's task.
* @hide
*/
- int TRANSIT_TASK_TO_FRONT = 10;
+ int TRANSIT_OLD_TASK_TO_FRONT = 10;
/**
* A window in an existing task is being put below all other tasks.
* @hide
*/
- int TRANSIT_TASK_TO_BACK = 11;
+ int TRANSIT_OLD_TASK_TO_BACK = 11;
/**
* A window in a new activity that doesn't have a wallpaper is being opened on top of one that
* does, effectively closing the wallpaper.
* @hide
*/
- int TRANSIT_WALLPAPER_CLOSE = 12;
+ int TRANSIT_OLD_WALLPAPER_CLOSE = 12;
/**
* A window in a new activity that does have a wallpaper is being opened on one that didn't,
* effectively opening the wallpaper.
* @hide
*/
- int TRANSIT_WALLPAPER_OPEN = 13;
+ int TRANSIT_OLD_WALLPAPER_OPEN = 13;
/**
* A window in a new activity is being opened on top of an existing one, and both are on top
* of the wallpaper.
* @hide
*/
- int TRANSIT_WALLPAPER_INTRA_OPEN = 14;
+ int TRANSIT_OLD_WALLPAPER_INTRA_OPEN = 14;
/**
* The window in the top-most activity is being closed to reveal the previous activity, and
* both are on top of the wallpaper.
* @hide
*/
- int TRANSIT_WALLPAPER_INTRA_CLOSE = 15;
+ int TRANSIT_OLD_WALLPAPER_INTRA_CLOSE = 15;
/**
* A window in a new task is being opened behind an existing one in another activity's task.
* The new window will show briefly and then be gone.
* @hide
*/
- int TRANSIT_TASK_OPEN_BEHIND = 16;
+ int TRANSIT_OLD_TASK_OPEN_BEHIND = 16;
/**
* An activity is being relaunched (e.g. due to configuration change).
* @hide
*/
- int TRANSIT_ACTIVITY_RELAUNCH = 18;
+ int TRANSIT_OLD_ACTIVITY_RELAUNCH = 18;
/**
* Keyguard is going away.
* @hide
*/
- int TRANSIT_KEYGUARD_GOING_AWAY = 20;
+ int TRANSIT_OLD_KEYGUARD_GOING_AWAY = 20;
/**
* Keyguard is going away with showing an activity behind that requests wallpaper.
* @hide
*/
- int TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21;
+ int TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21;
/**
* Keyguard is being occluded.
* @hide
*/
- int TRANSIT_KEYGUARD_OCCLUDE = 22;
+ int TRANSIT_OLD_KEYGUARD_OCCLUDE = 22;
/**
* Keyguard is being unoccluded.
* @hide
*/
- int TRANSIT_KEYGUARD_UNOCCLUDE = 23;
+ int TRANSIT_OLD_KEYGUARD_UNOCCLUDE = 23;
/**
* A translucent activity is being opened.
* @hide
*/
- int TRANSIT_TRANSLUCENT_ACTIVITY_OPEN = 24;
+ int TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN = 24;
/**
* A translucent activity is being closed.
* @hide
*/
- int TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE = 25;
+ int TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE = 25;
/**
* A crashing activity is being closed.
* @hide
*/
- int TRANSIT_CRASHING_ACTIVITY_CLOSE = 26;
+ int TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE = 26;
/**
* A task is changing windowing modes
* @hide
*/
- int TRANSIT_TASK_CHANGE_WINDOWING_MODE = 27;
+ int TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE = 27;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "TRANSIT_OLD_" }, value = {
+ TRANSIT_OLD_UNSET,
+ TRANSIT_OLD_NONE,
+ TRANSIT_OLD_ACTIVITY_OPEN,
+ TRANSIT_OLD_ACTIVITY_CLOSE,
+ TRANSIT_OLD_TASK_OPEN,
+ TRANSIT_OLD_TASK_CLOSE,
+ TRANSIT_OLD_TASK_TO_FRONT,
+ TRANSIT_OLD_TASK_TO_BACK,
+ TRANSIT_OLD_WALLPAPER_CLOSE,
+ TRANSIT_OLD_WALLPAPER_OPEN,
+ TRANSIT_OLD_WALLPAPER_INTRA_OPEN,
+ TRANSIT_OLD_WALLPAPER_INTRA_CLOSE,
+ TRANSIT_OLD_TASK_OPEN_BEHIND,
+ TRANSIT_OLD_ACTIVITY_RELAUNCH,
+ TRANSIT_OLD_KEYGUARD_GOING_AWAY,
+ TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+ TRANSIT_OLD_KEYGUARD_OCCLUDE,
+ TRANSIT_OLD_KEYGUARD_UNOCCLUDE,
+ TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN,
+ TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE,
+ TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE,
+ TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface TransitionOldType {}
+
+ /** @hide */
+ int TRANSIT_NONE = 0;
+ /** @hide */
+ int TRANSIT_OPEN = 1;
+ /** @hide */
+ int TRANSIT_CLOSE = 2;
+ /** @hide */
+ int TRANSIT_TO_FRONT = 3;
+ /** @hide */
+ int TRANSIT_TO_BACK = 4;
+ /** @hide */
+ int TRANSIT_RELAUNCH = 5;
+ /** @hide */
+ int TRANSIT_CHANGE_WINDOWING_MODE = 6;
+ /** @hide */
+ int TRANSIT_KEYGUARD_GOING_AWAY = 7;
+ /** @hide */
+ int TRANSIT_KEYGUARD_OCCLUDE = 8;
+ /** @hide */
+ int TRANSIT_KEYGUARD_UNOCCLUDE = 9;
/**
* @hide
*/
@IntDef(prefix = { "TRANSIT_" }, value = {
- TRANSIT_UNSET,
TRANSIT_NONE,
- TRANSIT_ACTIVITY_OPEN,
- TRANSIT_ACTIVITY_CLOSE,
- TRANSIT_TASK_OPEN,
- TRANSIT_TASK_CLOSE,
- TRANSIT_TASK_TO_FRONT,
- TRANSIT_TASK_TO_BACK,
- TRANSIT_WALLPAPER_CLOSE,
- TRANSIT_WALLPAPER_OPEN,
- TRANSIT_WALLPAPER_INTRA_OPEN,
- TRANSIT_WALLPAPER_INTRA_CLOSE,
- TRANSIT_TASK_OPEN_BEHIND,
- TRANSIT_ACTIVITY_RELAUNCH,
+ TRANSIT_OPEN,
+ TRANSIT_CLOSE,
+ TRANSIT_TO_FRONT,
+ TRANSIT_TO_BACK,
+ TRANSIT_RELAUNCH,
+ TRANSIT_CHANGE_WINDOWING_MODE,
TRANSIT_KEYGUARD_GOING_AWAY,
- TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
TRANSIT_KEYGUARD_OCCLUDE,
TRANSIT_KEYGUARD_UNOCCLUDE,
- TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
- TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE,
- TRANSIT_CRASHING_ACTIVITY_CLOSE,
- TRANSIT_TASK_CHANGE_WINDOWING_MODE
})
@Retention(RetentionPolicy.SOURCE)
@interface TransitionType {}
@@ -326,13 +366,20 @@
int TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION = 0x8;
/**
+ * Transition flag: App is crashed.
+ * @hide
+ */
+ int TRANSIT_FLAG_APP_CRASHED = 0x10;
+
+ /**
* @hide
*/
@IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = {
TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE,
TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION,
TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER,
- TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION
+ TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION,
+ TRANSIT_FLAG_APP_CRASHED
})
@Retention(RetentionPolicy.SOURCE)
@interface TransitionFlags {}
@@ -2096,6 +2143,21 @@
public static final int PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME = 0x40000000;
/**
+ * Flag to indicate that we want to intercept and handle global drag and drop for all users.
+ * This flag allows a window to considered for drag events even if not visible, and will
+ * receive drags for all active users in the system.
+ *
+ * Additional data is provided to windows with this flag, including the {@link ClipData}
+ * including all items with the {@link DragEvent#ACTION_DRAG_STARTED} event, and the
+ * actual drag surface with the {@link DragEvent#ACTION_DROP} event. If the window consumes,
+ * the drop, then the cleanup of the drag surface (provided as a part of
+ * {@link DragEvent#ACTION_DROP}) will be relinquished to the window.
+ * @hide
+ */
+ @RequiresPermission(permission.MANAGE_ACTIVITY_STACKS)
+ public static final int PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP = 0x80000000;
+
+ /**
* An internal annotation for flags that can be specified to {@link #softInputMode}.
*
* @hide
@@ -2245,7 +2307,11 @@
@ViewDebug.FlagToString(
mask = PRIVATE_FLAG_TRUSTED_OVERLAY,
equals = PRIVATE_FLAG_TRUSTED_OVERLAY,
- name = "TRUSTED_OVERLAY")
+ name = "TRUSTED_OVERLAY"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP,
+ equals = PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP,
+ name = "INTERCEPT_GLOBAL_DRAG_AND_DROP")
})
@PrivateFlags
@TestApi
@@ -4003,11 +4069,12 @@
/**
* Holds the WM lock for the specified amount of milliseconds.
* Intended for use by the tests that need to imitate lock contention.
+ * The token should be obtained by
+ * {@link android.content.pm.PackageManager#getHoldLockToken()}.
* @hide
*/
@TestApi
- @RequiresPermission(android.Manifest.permission.INJECT_EVENTS)
- default void holdLock(int durationMs) {
+ default void holdLock(IBinder token, int durationMs) {
throw new UnsupportedOperationException();
}
}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 7dfae00..4292a80 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -280,9 +280,9 @@
}
@Override
- public void holdLock(int durationMs) {
+ public void holdLock(IBinder token, int durationMs) {
try {
- WindowManagerGlobal.getWindowManagerService().holdLock(durationMs);
+ WindowManagerGlobal.getWindowManagerService().holdLock(token, durationMs);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 73636f8..093dfb4 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -19,7 +19,10 @@
import static android.view.OnReceiveContentCallback.Payload.SOURCE_INPUT_METHOD;
import android.annotation.CallSuper;
+import android.annotation.IntRange;
+import android.annotation.Nullable;
import android.content.ClipData;
+import android.content.ClipDescription;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
@@ -585,6 +588,48 @@
}
/**
+ * The default implementation returns the given amount of text around the current cursor
+ * position in the buffer.
+ */
+ @Nullable
+ public SurroundingText getSurroundingText(
+ @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) {
+ final Editable content = getEditable();
+ if (content == null) return null;
+
+ int selStart = Selection.getSelectionStart(content);
+ int selEnd = Selection.getSelectionEnd(content);
+
+ // Guard against the case where the cursor has not been positioned yet.
+ if (selStart < 0 || selEnd < 0) {
+ return null;
+ }
+
+ if (selStart > selEnd) {
+ int tmp = selStart;
+ selStart = selEnd;
+ selEnd = tmp;
+ }
+
+ int contentLength = content.length();
+ int startPos = selStart - beforeLength;
+ int endPos = selEnd + afterLength;
+
+ // Guards the start and end pos within range [0, contentLength].
+ startPos = Math.max(0, startPos);
+ endPos = Math.min(contentLength, endPos);
+
+ CharSequence surroundingText;
+ if ((flags & GET_TEXT_WITH_STYLES) != 0) {
+ surroundingText = content.subSequence(startPos, endPos);
+ } else {
+ surroundingText = TextUtils.substring(content, startPos, endPos);
+ }
+ return new SurroundingText(
+ surroundingText, selStart - startPos, selEnd - startPos, startPos);
+ }
+
+ /**
* The default implementation turns this into the enter key.
*/
public boolean performEditorAction(int actionCode) {
@@ -874,23 +919,18 @@
}
/**
- * Default implementation which invokes the target view's {@link OnReceiveContentCallback} if
- * it is {@link View#setOnReceiveContentCallback set} and supports the MIME type of the given
- * content; otherwise, simply returns false.
+ * Default implementation which invokes {@link View#onReceiveContent} on the target view if the
+ * MIME type of the content matches one of the MIME types returned by
+ * {@link View#getOnReceiveContentMimeTypes()}. If the MIME type of the content is not matched,
+ * returns false without any side effects.
*/
public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
- @SuppressWarnings("unchecked") final OnReceiveContentCallback<View> receiver =
- (OnReceiveContentCallback<View>) mTargetView.getOnReceiveContentCallback();
- if (receiver == null) {
+ ClipDescription description = inputContentInfo.getDescription();
+ final String[] viewMimeTypes = mTargetView.getOnReceiveContentMimeTypes();
+ if (viewMimeTypes == null || !description.hasMimeType(viewMimeTypes)) {
if (DEBUG) {
- Log.d(TAG, "Can't insert content from IME; no callback");
- }
- return false;
- }
- if (!receiver.supports(mTargetView, inputContentInfo.getDescription())) {
- if (DEBUG) {
- Log.d(TAG, "Can't insert content from IME; callback doesn't support MIME type: "
- + inputContentInfo.getDescription());
+ Log.d(TAG, "Can't insert content from IME; unsupported MIME type: content="
+ + description + ", viewMimeTypes=" + viewMimeTypes);
}
return false;
}
@@ -902,13 +942,13 @@
return false;
}
}
- final ClipData clip = new ClipData(inputContentInfo.getDescription(),
+ final ClipData clip = new ClipData(description,
new ClipData.Item(inputContentInfo.getContentUri()));
final OnReceiveContentCallback.Payload payload =
new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_INPUT_METHOD)
.setLinkUri(inputContentInfo.getLinkUri())
.setExtras(opts)
.build();
- return receiver.onReceiveContent(mTargetView, payload);
+ return mTargetView.onReceiveContent(payload);
}
}
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 4337ed5..c7acd29 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -16,14 +16,20 @@
package android.view.inputmethod;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.inputmethodservice.InputMethodService;
import android.os.Bundle;
import android.os.Handler;
+import android.text.TextUtils;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* The InputConnection interface is the communication channel from an
* {@link InputMethod} back to the application that is receiving its
@@ -122,14 +128,20 @@
* of each other, and the IME may use them however they see fit.</p>
*/
public interface InputConnection {
+ /** @hide */
+ @IntDef(flag = true, prefix = { "GET_TEXT_" }, value = {
+ GET_TEXT_WITH_STYLES,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface GetTextType {}
+
/**
- * Flag for use with {@link #getTextAfterCursor} and
- * {@link #getTextBeforeCursor} to have style information returned
- * along with the text. If not set, {@link #getTextAfterCursor}
- * sends only the raw text, without style or other spans. If set,
- * it may return a complex CharSequence of both text and style
- * spans. <strong>Editor authors</strong>: you should strive to
- * send text with styles if possible, but it is not required.
+ * Flag for use with {@link #getTextAfterCursor}, {@link #getTextBeforeCursor} and
+ * {@link #getSurroundingText} to have style information returned along with the text. If not
+ * set, {@link #getTextAfterCursor} sends only the raw text, without style or other spans. If
+ * set, it may return a complex CharSequence of both text and style spans.
+ * <strong>Editor authors</strong>: you should strive to send text with styles if possible, but
+ * it is not required.
*/
int GET_TEXT_WITH_STYLES = 0x0001;
@@ -264,6 +276,61 @@
CharSequence getSelectedText(int flags);
/**
+ * Gets the surrounding text around the current cursor, with <var>beforeLength</var> characters
+ * of text before the cursor (start of the selection), <var>afterLength</var> characters of text
+ * after the cursor (end of the selection), and all of the selected text.
+ *
+ * <p>This method may fail either if the input connection has become invalid (such as its
+ * process crashing), or the client is taking too long to respond with the text (it is given a
+ * couple seconds to return), or the protocol is not supported. In any of these cases, null is
+ * returned.
+ *
+ * <p>This method does not affect the text in the editor in any way, nor does it affect the
+ * selection or composing spans.</p>
+ *
+ * <p>If {@link #GET_TEXT_WITH_STYLES} is supplied as flags, the editor should return a
+ * {@link android.text.Spanned} with all the spans set on the text.</p>
+ *
+ * <p><strong>IME authors:</strong> please consider this will trigger an IPC round-trip that
+ * will take some time. Assume this method consumes a lot of time. If you are using this to get
+ * the initial surrounding text around the cursor, you may consider using
+ * {@link EditorInfo#getInitialTextBeforeCursor(int, int)},
+ * {@link EditorInfo#getInitialSelectedText(int)}, and
+ * {@link EditorInfo#getInitialTextAfterCursor(int, int)} to prevent IPC costs.</p>
+ *
+ * @param beforeLength The expected length of the text before the cursor.
+ * @param afterLength The expected length of the text after the cursor.
+ * @param flags Supplies additional options controlling how the text is returned. Defined by the
+ * constants.
+ * @return an {@link android.view.inputmethod.SurroundingText} object describing the surrounding
+ * text and state of selection, or null if the input connection is no longer valid, or the
+ * editor can't comply with the request for some reason, or the application does not implement
+ * this method. The length of the returned text might be less than the sum of
+ * <var>beforeLength</var> and <var>afterLength</var> .
+ */
+ @Nullable
+ default SurroundingText getSurroundingText(
+ @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength,
+ @GetTextType int flags) {
+ CharSequence textBeforeCursor = getTextBeforeCursor(beforeLength, flags);
+ if (textBeforeCursor == null) {
+ textBeforeCursor = "";
+ }
+ CharSequence selectedText = getSelectedText(flags);
+ if (selectedText == null) {
+ selectedText = "";
+ }
+ CharSequence textAfterCursor = getTextAfterCursor(afterLength, flags);
+ if (textAfterCursor == null) {
+ textAfterCursor = "";
+ }
+ CharSequence surroundingText =
+ TextUtils.concat(textBeforeCursor, selectedText, textAfterCursor);
+ return new SurroundingText(surroundingText, textBeforeCursor.length(),
+ textBeforeCursor.length() + selectedText.length(), -1);
+ }
+
+ /**
* Retrieve the current capitalization mode in effect at the
* current cursor position in the text. See
* {@link android.text.TextUtils#getCapsMode TextUtils.getCapsMode}
diff --git a/core/java/android/view/inputmethod/InputConnectionInspector.java b/core/java/android/view/inputmethod/InputConnectionInspector.java
index 5f25bf5..7621da7 100644
--- a/core/java/android/view/inputmethod/InputConnectionInspector.java
+++ b/core/java/android/view/inputmethod/InputConnectionInspector.java
@@ -44,6 +44,7 @@
MissingMethodFlags.GET_HANDLER,
MissingMethodFlags.CLOSE_CONNECTION,
MissingMethodFlags.COMMIT_CONTENT,
+ MissingMethodFlags.GET_SURROUNDING_TEXT
})
public @interface MissingMethodFlags {
/**
@@ -86,6 +87,11 @@
* {@link android.os.Build.VERSION_CODES#N} MR-1 and later.
*/
int COMMIT_CONTENT = 1 << 7;
+ /**
+ * {@link InputConnection#getSurroundingText(int, int, int)} is available in
+ * {@link android.os.Build.VERSION_CODES#S} and later.
+ */
+ int GET_SURROUNDING_TEXT = 1 << 8;
}
private static final Map<Class, Integer> sMissingMethodsMap = Collections.synchronizedMap(
@@ -138,6 +144,9 @@
if (!hasCommitContent(clazz)) {
flags |= MissingMethodFlags.COMMIT_CONTENT;
}
+ if (!hasGetSurroundingText(clazz)) {
+ flags |= MissingMethodFlags.GET_SURROUNDING_TEXT;
+ }
sMissingMethodsMap.put(clazz, flags);
return flags;
}
@@ -216,6 +225,16 @@
}
}
+ private static boolean hasGetSurroundingText(@NonNull final Class clazz) {
+ try {
+ final Method method = clazz.getMethod("getSurroundingText", int.class, int.class,
+ int.class);
+ return !Modifier.isAbstract(method.getModifiers());
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
public static String getMissingMethodFlagsAsString(@MissingMethodFlags final int flags) {
final StringBuilder sb = new StringBuilder();
boolean isEmpty = true;
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index f671e22..ec7fa60 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -16,6 +16,7 @@
package android.view.inputmethod;
+import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Handler;
import android.view.KeyEvent;
@@ -101,6 +102,16 @@
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
+ @Nullable
+ @Override
+ public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
+ return mTarget.getSurroundingText(beforeLength, afterLength, flags);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws NullPointerException if the target is {@code null}.
+ */
@Override
public int getCursorCapsMode(int reqModes) {
return mTarget.getCursorCapsMode(reqModes);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java b/core/java/android/view/inputmethod/SurroundingText.aidl
similarity index 77%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java
copy to core/java/android/view/inputmethod/SurroundingText.aidl
index 273bd27..7a9898e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java
+++ b/core/java/android/view/inputmethod/SurroundingText.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 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.
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-package com.android.wm.shell;
+package android.view.inputmethod;
-/**
- * Interface for the shell.
- */
-public class WindowManagerShell {
-}
+parcelable SurroundingText;
\ No newline at end of file
diff --git a/core/java/android/view/inputmethod/SurroundingText.java b/core/java/android/view/inputmethod/SurroundingText.java
new file mode 100644
index 0000000..506f95a
--- /dev/null
+++ b/core/java/android/view/inputmethod/SurroundingText.java
@@ -0,0 +1,150 @@
+/*
+ * 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.inputmethod;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * Information about the surrounding text around the cursor for use by an input method.
+ *
+ * <p>This contains information about the text and the selection relative to the text. </p>
+ */
+public final class SurroundingText implements Parcelable {
+ /**
+ * The surrounding text around the cursor.
+ */
+ @NonNull
+ private final CharSequence mText;
+
+ /**
+ * The text offset of the start of the selection in the surrounding text.
+ *
+ * <p>This needs to be the position relative to the {@link #mText} instead of the real position
+ * in the editor.</p>
+ */
+ @IntRange(from = 0)
+ private final int mSelectionStart;
+
+ /**
+ * The text offset of the end of the selection in the surrounding text.
+ *
+ * <p>This needs to be the position relative to the {@link #mText} instead of the real position
+ * in the editor.</p>
+ */
+ @IntRange(from = 0)
+ private final int mSelectionEnd;
+
+ /**
+ * The text offset between the start of the editor's text and the start of the surrounding text.
+ *
+ * <p>-1 indicates the offset information is unknown.</p>
+ */
+ @IntRange(from = -1)
+ private final int mOffset;
+
+ /**
+ * Constructor.
+ *
+ * @param text The surrounding text.
+ * @param selectionStart The text offset of the start of the selection in the surrounding text.
+ * Reversed selection is allowed.
+ * @param selectionEnd The text offset of the end of the selection in the surrounding text.
+ * Reversed selection is allowed.
+ * @param offset The text offset between the start of the editor's text and the start of the
+ * surrounding text. -1 indicates the offset is unknown.
+ */
+ public SurroundingText(@NonNull final CharSequence text,
+ @IntRange(from = 0) int selectionStart, @IntRange(from = 0) int selectionEnd,
+ @IntRange(from = -1) int offset) {
+ mText = text;
+ mSelectionStart = selectionStart;
+ mSelectionEnd = selectionEnd;
+ mOffset = offset;
+ }
+
+ /**
+ * Returns the surrounding text around the cursor.
+ */
+ @NonNull
+ public CharSequence getText() {
+ return mText;
+ }
+
+ /**
+ * Returns the text offset of the start of the selection in the surrounding text.
+ */
+ @IntRange(from = 0)
+ public int getSelectionStart() {
+ return mSelectionStart;
+ }
+
+ /**
+ * Returns the text offset of the end of the selection in the surrounding text.
+ */
+ @IntRange(from = 0)
+ public int getSelectionEnd() {
+ return mSelectionEnd;
+ }
+
+ /**
+ * Returns text offset between the start of the editor's text and the start of the surrounding
+ * text.
+ *
+ * <p>-1 indicates the offset information is unknown.</p>
+ */
+ @IntRange(from = -1)
+ public int getOffset() {
+ return mOffset;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ TextUtils.writeToParcel(mText, out, flags);
+ out.writeInt(mSelectionStart);
+ out.writeInt(mSelectionEnd);
+ out.writeInt(mOffset);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<SurroundingText> CREATOR =
+ new Parcelable.Creator<SurroundingText>() {
+ @NonNull
+ public SurroundingText createFromParcel(Parcel in) {
+ final CharSequence text =
+ TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ final int selectionHead = in.readInt();
+ final int selectionEnd = in.readInt();
+ final int offset = in.readInt();
+ return new SurroundingText(
+ text == null ? "" : text, selectionHead, selectionEnd, offset);
+ }
+
+ @NonNull
+ public SurroundingText[] newArray(int size) {
+ return new SurroundingText[size];
+ }
+ };
+}
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index 8eac1c1..2c844eb 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -89,7 +89,7 @@
try {
request.setSystemTextClassifierMetadata(mSystemTcMetadata);
final BlockingCallback<TextSelection> callback =
- new BlockingCallback<>("textselection");
+ new BlockingCallback<>("textselection", mSettings);
mManagerService.onSuggestSelection(mSessionId, request, callback);
final TextSelection selection = callback.get();
if (selection != null) {
@@ -112,7 +112,7 @@
try {
request.setSystemTextClassifierMetadata(mSystemTcMetadata);
final BlockingCallback<TextClassification> callback =
- new BlockingCallback<>("textclassification");
+ new BlockingCallback<>("textclassification", mSettings);
mManagerService.onClassifyText(mSessionId, request, callback);
final TextClassification classification = callback.get();
if (classification != null) {
@@ -142,7 +142,7 @@
try {
request.setSystemTextClassifierMetadata(mSystemTcMetadata);
final BlockingCallback<TextLinks> callback =
- new BlockingCallback<>("textlinks");
+ new BlockingCallback<>("textlinks", mSettings);
mManagerService.onGenerateLinks(mSessionId, request, callback);
final TextLinks links = callback.get();
if (links != null) {
@@ -193,7 +193,7 @@
try {
request.setSystemTextClassifierMetadata(mSystemTcMetadata);
final BlockingCallback<TextLanguage> callback =
- new BlockingCallback<>("textlanguage");
+ new BlockingCallback<>("textlanguage", mSettings);
mManagerService.onDetectLanguage(mSessionId, request, callback);
final TextLanguage textLanguage = callback.get();
if (textLanguage != null) {
@@ -213,7 +213,7 @@
try {
request.setSystemTextClassifierMetadata(mSystemTcMetadata);
final BlockingCallback<ConversationActions> callback =
- new BlockingCallback<>("conversation-actions");
+ new BlockingCallback<>("conversation-actions", mSettings);
mManagerService.onSuggestConversationActions(mSessionId, request, callback);
final ConversationActions conversationActions = callback.get();
if (conversationActions != null) {
@@ -279,8 +279,8 @@
extends ITextClassifierCallback.Stub {
private final ResponseReceiver<T> mReceiver;
- BlockingCallback(String name) {
- mReceiver = new ResponseReceiver<>(name);
+ BlockingCallback(String name, TextClassificationConstants settings) {
+ mReceiver = new ResponseReceiver<>(name, settings);
}
@Override
@@ -303,10 +303,12 @@
private final CountDownLatch mLatch = new CountDownLatch(1);
private final String mName;
+ private final TextClassificationConstants mSettings;
private T mResponse;
- private ResponseReceiver(String name) {
+ private ResponseReceiver(String name, TextClassificationConstants settings) {
mName = name;
+ mSettings = settings;
}
public void onSuccess(T response) {
@@ -327,7 +329,9 @@
// NOTE that TextClassifier calls should preferably always be called on a worker thread.
if (Looper.myLooper() != Looper.getMainLooper()) {
try {
- boolean success = mLatch.await(2, TimeUnit.SECONDS);
+ boolean success = mLatch.await(
+ mSettings.getSystemTextClassifierApiTimeoutInSecond(),
+ TimeUnit.SECONDS);
if (!success) {
Log.w(LOG_TAG, "Timeout in ResponseReceiver.get(): " + mName);
}
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index adb6fea..2975afc 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -82,6 +82,14 @@
static final String TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE =
"textclassifier_service_package_override";
+ /**
+ * The timeout value in seconds used by {@link SystemTextClassifier} for each TextClassifier
+ * API calls.
+ */
+ @VisibleForTesting
+ static final String SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND =
+ "system_textclassifier_api_timeout_in_second";
+
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;
@@ -91,6 +99,7 @@
private static final boolean SMART_LINKIFY_ENABLED_DEFAULT = true;
private static final boolean SMART_SELECT_ANIMATION_ENABLED_DEFAULT = true;
private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000;
+ private static final long SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND_DEFAULT = 60;
@Nullable
public String getTextClassifierServicePackageOverride() {
@@ -140,27 +149,27 @@
GENERATE_LINKS_MAX_TEXT_LENGTH, GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT);
}
+ public long getSystemTextClassifierApiTimeoutInSecond() {
+ return DeviceConfig.getLong(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND,
+ SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND_DEFAULT);
+ }
+
void dump(IndentingPrintWriter pw) {
pw.println("TextClassificationConstants:");
pw.increaseIndent();
- pw.printPair("generate_links_max_text_length", getGenerateLinksMaxTextLength())
- .println();
- pw.printPair("local_textclassifier_enabled", isLocalTextClassifierEnabled())
- .println();
- pw.printPair("model_dark_launch_enabled", isModelDarkLaunchEnabled())
- .println();
- pw.printPair("smart_linkify_enabled", isSmartLinkifyEnabled())
- .println();
- pw.printPair("smart_select_animation_enabled", isSmartSelectionAnimationEnabled())
- .println();
- pw.printPair("smart_selection_enabled", isSmartSelectionEnabled())
- .println();
- pw.printPair("smart_text_share_enabled", isSmartTextShareEnabled())
- .println();
- pw.printPair("system_textclassifier_enabled", isSystemTextClassifierEnabled())
- .println();
- pw.printPair("textclassifier_service_package_override",
+ pw.print(GENERATE_LINKS_MAX_TEXT_LENGTH, getGenerateLinksMaxTextLength()).println();
+ pw.print(LOCAL_TEXT_CLASSIFIER_ENABLED, isLocalTextClassifierEnabled()).println();
+ pw.print(MODEL_DARK_LAUNCH_ENABLED, isModelDarkLaunchEnabled()).println();
+ pw.print(SMART_LINKIFY_ENABLED, isSmartLinkifyEnabled()).println();
+ pw.print(SMART_SELECT_ANIMATION_ENABLED, isSmartSelectionAnimationEnabled()).println();
+ pw.print(SMART_SELECTION_ENABLED, isSmartSelectionEnabled()).println();
+ pw.print(SMART_TEXT_SHARE_ENABLED, isSmartTextShareEnabled()).println();
+ pw.print(SYSTEM_TEXT_CLASSIFIER_ENABLED, isSystemTextClassifierEnabled()).println();
+ pw.print(TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE,
getTextClassifierServicePackageOverride()).println();
+ pw.print(SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND,
+ getSystemTextClassifierApiTimeoutInSecond()).println();
pw.decreaseIndent();
}
}
\ No newline at end of file
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 97e0689..45b21c5 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -82,6 +82,7 @@
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.SurroundingText;
import android.view.inspector.InspectableProperty;
import android.view.inspector.InspectableProperty.EnumEntry;
import android.widget.RemoteViews.OnClickHandler;
@@ -5997,6 +5998,12 @@
}
@Override
+ public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
+ if (mTarget == null) return null;
+ return mTarget.getSurroundingText(beforeLength, afterLength, flags);
+ }
+
+ @Override
public int getCursorCapsMode(int reqModes) {
if (mTarget == null) return InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
return mTarget.getCursorCapsMode(reqModes);
diff --git a/core/java/android/widget/RadioButton.java b/core/java/android/widget/RadioButton.java
index 3e26f63..a04d7c3 100644
--- a/core/java/android/widget/RadioButton.java
+++ b/core/java/android/widget/RadioButton.java
@@ -16,10 +16,13 @@
package android.widget;
+import android.annotation.NonNull;
import android.content.Context;
import android.util.AttributeSet;
import android.view.accessibility.AccessibilityNodeInfo;
+import com.android.internal.R;
+
/**
* <p>
@@ -98,4 +101,15 @@
}
}
}
+
+ /** @hide **/
+ @Override
+ @NonNull
+ protected CharSequence getButtonStateDescription() {
+ if (isChecked()) {
+ return getResources().getString(R.string.selected);
+ } else {
+ return getResources().getString(R.string.not_selected);
+ }
+ }
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 7bb2b7e..5280a48 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8747,12 +8747,7 @@
outAttrs.initialSelEnd = getSelectionEnd();
outAttrs.initialCapsMode = ic.getCursorCapsMode(getInputType());
outAttrs.setInitialSurroundingText(mText);
- // If a custom `OnReceiveContentCallback` is set, pass its supported MIME types.
- OnReceiveContentCallback<TextView> receiver = getOnReceiveContentCallback();
- if (receiver != null) {
- outAttrs.contentMimeTypes = receiver.getSupportedMimeTypes(this)
- .toArray(new String[0]);
- }
+ outAttrs.contentMimeTypes = getOnReceiveContentMimeTypes();
return ic;
}
}
@@ -11051,12 +11046,12 @@
MotionEvent.actionToString(event.getActionMasked()),
event.getX(), event.getY());
}
- if (!isFromPrimePointer(event, false)) {
- return true;
- }
-
final int action = event.getActionMasked();
if (mEditor != null) {
+ if (!isFromPrimePointer(event, false)) {
+ return true;
+ }
+
mEditor.onTouchEvent(event);
if (mEditor.mInsertionPointCursorController != null
@@ -13735,20 +13730,6 @@
}
/**
- * Returns the callback used for handling insertion of content into this view. See
- * {@link #setOnReceiveContentCallback} for more info.
- *
- * @return The callback for handling insertion of content. Returns null if no callback has been
- * {@link #setOnReceiveContentCallback set}.
- */
- @SuppressWarnings("unchecked")
- @Nullable
- @Override
- public OnReceiveContentCallback<TextView> getOnReceiveContentCallback() {
- return (OnReceiveContentCallback<TextView>) super.getOnReceiveContentCallback();
- }
-
- /**
* Sets the callback to handle insertion of content into this view.
*
* <p>This callback will be invoked for the following scenarios:
@@ -13761,32 +13742,51 @@
* <li>{@link Intent#ACTION_PROCESS_TEXT} replacement
* </ol>
*
- * <p>The callback will only be invoked if the MIME type of the content is
- * {@link OnReceiveContentCallback#getSupportedMimeTypes declared as supported} by the callback.
- * If the content type is not supported by the callback, the default platform handling will be
- * executed instead.
+ * <p>This callback is only invoked for content whose MIME type matches a type specified via
+ * the {code mimeTypes} parameter. If the MIME type is not supported by the callback, the
+ * default platform handling will be executed instead (no-op for the default {@link View}).
*
+ * <p><em>Note: MIME type matching in the Android framework is case-sensitive, unlike formal RFC
+ * MIME types. As a result, you should always write your MIME types with lower case letters, or
+ * use {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to lower
+ * case.</em>
+ *
+ * @param mimeTypes The type of content for which the callback should be invoked. This may use
+ * wildcards such as "text/*", "image/*", etc. This must not be null or empty if a non-null
+ * callback is passed in.
* @param callback The callback to use. This can be null to reset to the default behavior.
*/
+ @SuppressWarnings("rawtypes")
@Override
public void setOnReceiveContentCallback(
- @Nullable OnReceiveContentCallback<? extends View> callback) {
- super.setOnReceiveContentCallback(callback);
+ @Nullable String[] mimeTypes,
+ @Nullable OnReceiveContentCallback callback) {
+ super.setOnReceiveContentCallback(mimeTypes, callback);
}
/**
- * Handles the request to insert content using the configured callback or the default callback.
+ * Receives the given content. The default implementation invokes the callback set via
+ * {@link #setOnReceiveContentCallback}. If no callback is set or if the callback does not
+ * support the given content (based on the MIME type), executes the default platform handling
+ * (e.g. coerces content to text if the source is
+ * {@link OnReceiveContentCallback.Payload#SOURCE_CLIPBOARD} and this is an editable
+ * {@link TextView}).
*
- * @hide
+ * @param payload The content to insert and related metadata.
+ *
+ * @return Returns true if the content was handled in some way, false otherwise. Actual
+ * insertion may be processed asynchronously in the background and may or may not succeed even
+ * if this method returns true. For example, an app may not end up inserting an item if it
+ * exceeds the app's size limit for that type of content.
*/
- void onReceiveContent(@NonNull OnReceiveContentCallback.Payload payload) {
- OnReceiveContentCallback<TextView> receiver = getOnReceiveContentCallback();
- ClipDescription description = payload.getClip().getDescription();
- if (receiver != null && receiver.supports(this, description)) {
- receiver.onReceiveContent(this, payload);
+ @Override
+ public boolean onReceiveContent(@NonNull OnReceiveContentCallback.Payload payload) {
+ if (super.onReceiveContent(payload)) {
+ return true;
} else if (mEditor != null) {
- mEditor.getDefaultOnReceiveContentCallback().onReceiveContent(this, payload);
+ return mEditor.getDefaultOnReceiveContentCallback().onReceiveContent(this, payload);
}
+ return false;
}
private static void logCursor(String location, @Nullable String msgFormat, Object ... msgArgs) {
diff --git a/core/java/android/widget/TextViewOnReceiveContentCallback.java b/core/java/android/widget/TextViewOnReceiveContentCallback.java
index d7c95b7..7ed70ec 100644
--- a/core/java/android/widget/TextViewOnReceiveContentCallback.java
+++ b/core/java/android/widget/TextViewOnReceiveContentCallback.java
@@ -20,12 +20,12 @@
import static android.view.OnReceiveContentCallback.Payload.FLAG_CONVERT_TO_PLAIN_TEXT;
import static android.view.OnReceiveContentCallback.Payload.SOURCE_AUTOFILL;
import static android.view.OnReceiveContentCallback.Payload.SOURCE_DRAG_AND_DROP;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_INPUT_METHOD;
import static java.util.Collections.singleton;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
@@ -72,16 +72,6 @@
@Nullable private InputConnectionInfo mInputConnectionInfo;
@Nullable private ArraySet<String> mCachedSupportedMimeTypes;
- @SuppressLint("CallbackMethodName")
- @NonNull
- @Override
- public Set<String> getSupportedMimeTypes(@NonNull TextView view) {
- if (!isUsageOfImeCommitContentEnabled(view)) {
- return MIME_TYPES_ALL_TEXT;
- }
- return getSupportedMimeTypesAugmentedWithImeCommitContentMimeTypes();
- }
-
@Override
public boolean onReceiveContent(@NonNull TextView view, @NonNull Payload payload) {
if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
@@ -90,6 +80,11 @@
ClipData clip = payload.getClip();
@Source int source = payload.getSource();
@Flags int flags = payload.getFlags();
+ if (source == SOURCE_INPUT_METHOD) {
+ // InputConnection.commitContent() should only be used for non-text input which is not
+ // supported by the default implementation.
+ return false;
+ }
if (source == SOURCE_AUTOFILL) {
return onReceiveForAutofill(view, clip, flags);
}
@@ -123,7 +118,7 @@
}
}
}
- return true;
+ return didFirst;
}
private static void replaceSelection(@NonNull Editable editable,
@@ -160,7 +155,7 @@
@NonNull ClipData clip, @Flags int flags) {
final CharSequence text = coerceToText(clip, textView.getContext(), flags);
if (text.length() == 0) {
- return true;
+ return false;
}
replaceSelection((Editable) textView.getText(), text);
return true;
@@ -205,7 +200,7 @@
* non-text content.
*/
private static boolean isUsageOfImeCommitContentEnabled(@NonNull View view) {
- if (view.getOnReceiveContentCallback() != null) {
+ if (view.getOnReceiveContentMimeTypes() != null) {
if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) {
Log.v(LOG_TAG, "Fallback to commitContent disabled (custom callback is set)");
}
@@ -267,6 +262,17 @@
mInputConnectionInfo = null;
}
+ // TODO(b/168253885): Use this to populate the assist structure for Autofill
+
+ /** @hide */
+ @VisibleForTesting
+ public Set<String> getMimeTypes(TextView view) {
+ if (!isUsageOfImeCommitContentEnabled(view)) {
+ return MIME_TYPES_ALL_TEXT;
+ }
+ return getSupportedMimeTypesAugmentedWithImeCommitContentMimeTypes();
+ }
+
private Set<String> getSupportedMimeTypesAugmentedWithImeCommitContentMimeTypes() {
InputConnectionInfo icInfo = mInputConnectionInfo;
if (icInfo == null) {
@@ -291,7 +297,8 @@
}
/**
- * We want to avoid creating a new set on every invocation of {@link #getSupportedMimeTypes}.
+ * We want to avoid creating a new set on every invocation of
+ * {@link #getSupportedMimeTypesAugmentedWithImeCommitContentMimeTypes()}.
* This method will check if the cached set of MIME types matches the data in the given array
* from {@link EditorInfo} or if a new set should be created. The custom logic is needed for
* comparing the data because the set contains the additional "text/*" MIME type.
diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl
index b5a11b1..4a43a43 100644
--- a/core/java/android/window/ITaskOrganizerController.aidl
+++ b/core/java/android/window/ITaskOrganizerController.aidl
@@ -39,12 +39,8 @@
*/
void unregisterTaskOrganizer(ITaskOrganizer organizer);
- /**
- * Creates a persistent root task in WM for a particular windowing-mode.
- * {@link TaskOrganizer#onTaskAppeared} won't be called since we are returning
- * {@link TaskAppearedInfo} here.
- */
- TaskAppearedInfo createRootTask(int displayId, int windowingMode);
+ /** Creates a persistent root task in WM for a particular windowing-mode. */
+ void createRootTask(int displayId, int windowingMode, IBinder launchCookie);
/** Deletes a persistent root task in WM */
boolean deleteRootTask(in WindowContainerToken task);
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 5c86e1c..ad48a9f 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -23,12 +23,14 @@
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.app.ActivityManager;
+import android.os.IBinder;
import android.os.RemoteException;
import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* Interface for ActivityTaskManager/WindowManager to delegate control of tasks.
@@ -37,15 +39,19 @@
@TestApi
public class TaskOrganizer extends WindowOrganizer {
- private ITaskOrganizerController mTaskOrganizerController;
+ private final ITaskOrganizerController mTaskOrganizerController;
+ // Callbacks WM Core are posted on this executor if it isn't null, otherwise direct calls are
+ // made on the incoming binder call.
+ private final Executor mExecutor;
public TaskOrganizer() {
- this(null);
+ this(null /*taskOrganizerController*/, null /*executor*/);
}
/** @hide */
@VisibleForTesting
- public TaskOrganizer(ITaskOrganizerController taskOrganizerController) {
+ public TaskOrganizer(ITaskOrganizerController taskOrganizerController, Executor executor) {
+ mExecutor = executor != null ? executor : command -> command.run();
mTaskOrganizerController = taskOrganizerController != null
? taskOrganizerController : getController();
}
@@ -96,12 +102,18 @@
@BinderThread
public void onBackPressedOnTaskRoot(@NonNull ActivityManager.RunningTaskInfo taskInfo) {}
- /** Creates a persistent root task in WM for a particular windowing-mode. */
+ /**
+ * Creates a persistent root task in WM for a particular windowing-mode.
+ * @param displayId The display to create the root task on.
+ * @param windowingMode Windowing mode to put the root task in.
+ * @param launchCookie Launch cookie to associate with the task so that is can be identified
+ * when the {@link ITaskOrganizer#onTaskAppeared} callback is called.
+ */
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
@Nullable
- public TaskAppearedInfo createRootTask(int displayId, int windowingMode) {
+ public void createRootTask(int displayId, int windowingMode, @Nullable IBinder launchCookie) {
try {
- return mTaskOrganizerController.createRootTask(displayId, windowingMode);
+ mTaskOrganizerController.createRootTask(displayId, windowingMode, launchCookie);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -183,22 +195,22 @@
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
- TaskOrganizer.this.onTaskAppeared(taskInfo, leash);
+ mExecutor.execute(() -> TaskOrganizer.this.onTaskAppeared(taskInfo, leash));
}
@Override
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
- TaskOrganizer.this.onTaskVanished(taskInfo);
+ mExecutor.execute(() -> TaskOrganizer.this.onTaskVanished(taskInfo));
}
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
- TaskOrganizer.this.onTaskInfoChanged(info);
+ mExecutor.execute(() -> TaskOrganizer.this.onTaskInfoChanged(info));
}
@Override
public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo info) {
- TaskOrganizer.this.onBackPressedOnTaskRoot(info);
+ mExecutor.execute(() -> TaskOrganizer.this.onBackPressedOnTaskRoot(info));
}
};
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 34d1d4e..50174df 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -63,11 +63,11 @@
})
public @interface TransitionMode {}
- private final @WindowManager.TransitionType int mType;
+ private final @WindowManager.TransitionOldType int mType;
private final ArrayList<Change> mChanges = new ArrayList<>();
/** @hide */
- public TransitionInfo(@WindowManager.TransitionType int type) {
+ public TransitionInfo(@WindowManager.TransitionOldType int type) {
mType = type;
}
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index d8eaeda..52dc7e6 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -191,8 +191,9 @@
mOnUnsuspend = intent.getParcelableExtra(EXTRA_UNSUSPEND_INTENT);
if (mSuppliedDialogInfo != null) {
try {
- mSuspendingAppResources = mPm.getResourcesForApplicationAsUser(mSuspendingPackage,
- mUserId);
+ mSuspendingAppResources = createContextAsUser(
+ UserHandle.of(mUserId), /* flags */ 0).getPackageManager()
+ .getResourcesForApplication(mSuspendedPackage);
} catch (PackageManager.NameNotFoundException ne) {
Slog.e(TAG, "Could not find resources for " + mSuspendingPackage, ne);
}
diff --git a/core/java/com/android/internal/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java
index 5e886a6..7a87be3 100644
--- a/core/java/com/android/internal/compat/ChangeReporter.java
+++ b/core/java/com/android/internal/compat/ChangeReporter.java
@@ -16,6 +16,8 @@
package com.android.internal.compat;
+import static android.text.TextUtils.formatSimple;
+
import android.annotation.IntDef;
import android.util.Log;
import android.util.Slog;
@@ -175,7 +177,7 @@
}
private void debugLog(int uid, long changeId, int state) {
- String message = String.format("Compat change id reported: %d; UID %d; state: %s", changeId,
+ String message = formatSimple("Compat change id reported: %d; UID %d; state: %s", changeId,
uid, stateToString(state));
if (mSource == SOURCE_SYSTEM_SERVER) {
Slog.d(TAG, message);
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 3682b7b..af666d8 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -37,6 +37,7 @@
* updating, and disappearing and reappearing on the SD card.
*/
public abstract class PackageMonitor extends android.content.BroadcastReceiver {
+ static final String TAG = "PackageMonitor";
static final IntentFilter sPackageFilt = new IntentFilter();
static final IntentFilter sNonDataFilt = new IntentFilter();
static final IntentFilter sExternalFilt = new IntentFilter();
@@ -48,6 +49,9 @@
sPackageFilt.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
sPackageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
sPackageFilt.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
+ sPackageFilt.addAction(Intent.ACTION_PACKAGE_STARTABLE);
+ sPackageFilt.addAction(Intent.ACTION_PACKAGE_UNSTARTABLE);
+ sPackageFilt.addAction(Intent.ACTION_PACKAGE_FULLY_LOADED);
sPackageFilt.addDataScheme("package");
sNonDataFilt.addAction(Intent.ACTION_UID_REMOVED);
sNonDataFilt.addAction(Intent.ACTION_USER_STOPPED);
@@ -305,6 +309,13 @@
public void onPackageDataCleared(String packageName, int uid) {
}
+ /**
+ * Callback to indicate the package's state has changed.
+ * @param packageName Name of an installed package
+ * @param uid The UID the package runs under.
+ */
+ public void onPackageStateChanged(String packageName, int uid) {}
+
public int getChangingUserId() {
return mChangeUserId;
}
@@ -452,12 +463,21 @@
String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
mSomePackagesChanged = true;
onPackagesUnsuspended(pkgList);
+ } else if (Intent.ACTION_PACKAGE_STARTABLE.equals(action)
+ || Intent.ACTION_PACKAGE_UNSTARTABLE.equals(action)
+ || Intent.ACTION_PACKAGE_FULLY_LOADED.equals(action)) {
+ String pkg = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+ int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
+ mSomePackagesChanged = false;
+ if (pkg != null) {
+ onPackageStateChanged(pkg, uid);
+ }
}
if (mSomePackagesChanged) {
onSomePackagesChanged();
}
-
+
onFinishPackageChanges();
mChangeUserId = UserHandle.USER_NULL;
}
diff --git a/core/java/com/android/internal/inputmethod/CancellationGroup.java b/core/java/com/android/internal/inputmethod/CancellationGroup.java
index 09c9d12..a4a2208 100644
--- a/core/java/com/android/internal/inputmethod/CancellationGroup.java
+++ b/core/java/com/android/internal/inputmethod/CancellationGroup.java
@@ -263,6 +263,16 @@
super(factory);
}
}
+
+ /**
+ * Completable object of {@link android.view.inputmethod.SurroundingText}.
+ */
+ public static final class SurroundingText
+ extends Values<android.view.inputmethod.SurroundingText> {
+ private SurroundingText(@NonNull CancellationGroup factory) {
+ super(factory);
+ }
+ }
}
/**
@@ -292,6 +302,16 @@
return new Completable.ExtractedText(this);
}
+ /**
+ * @return an instance of {@link Completable.SurroundingText} that is associated with this
+ * {@link CancellationGroup}.
+ */
+ @AnyThread
+ public Completable.SurroundingText createCompletableSurroundingText() {
+ return new Completable.SurroundingText(this);
+ }
+
+
@AnyThread
private boolean registerLatch(@NonNull CountDownLatch latch) {
synchronized (mLock) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java b/core/java/com/android/internal/inputmethod/ISurroundingTextResultCallback.aidl
similarity index 69%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java
copy to core/java/com/android/internal/inputmethod/ISurroundingTextResultCallback.aidl
index 273bd27..6c4f3d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java
+++ b/core/java/com/android/internal/inputmethod/ISurroundingTextResultCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 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.
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package com.android.wm.shell;
+package com.android.internal.inputmethod;
-/**
- * Interface for the shell.
- */
-public class WindowManagerShell {
-}
+import android.view.inputmethod.SurroundingText;
+
+oneway interface ISurroundingTextResultCallback {
+ void onResult(in SurroundingText result);
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/ResultCallbacks.java b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
index 44a8a83..5eba898 100644
--- a/core/java/com/android/internal/inputmethod/ResultCallbacks.java
+++ b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
@@ -129,4 +129,32 @@
}
};
}
+
+ /**
+ * Creates {@link ISurroundingTextResultCallback.Stub} that is to set
+ * {@link CancellationGroup.Completable.SurroundingText} when receiving the result.
+ *
+ * @param value {@link CancellationGroup.Completable.SurroundingText} to be set when receiving
+ * the result.
+ * @return {@link ISurroundingTextResultCallback.Stub} that can be passed as a binder IPC
+ * parameter.
+ */
+ @AnyThread
+ public static ISurroundingTextResultCallback.Stub of(
+ @NonNull CancellationGroup.Completable.SurroundingText value) {
+ final AtomicReference<WeakReference<CancellationGroup.Completable.SurroundingText>>
+ atomicRef = new AtomicReference<>(new WeakReference<>(value));
+
+ return new ISurroundingTextResultCallback.Stub() {
+ @BinderThread
+ @Override
+ public void onResult(android.view.inputmethod.SurroundingText result) {
+ final CancellationGroup.Completable.SurroundingText value = unwrap(atomicRef);
+ if (value == null) {
+ return;
+ }
+ value.onComplete(result);
+ }
+ };
+ }
}
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index 7dc3871..c0648ab 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -70,7 +70,8 @@
intent.setClassName(DIALOGS_PACKAGE, DIALOGS_PACKAGE + ".ManageDialog");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY |
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- return PendingIntent.getActivityAsUser(context, 0, intent, 0, null, UserHandle.CURRENT);
+ return PendingIntent.getActivityAsUser(context, 0 /* requestCode */, intent,
+ PendingIntent.FLAG_IMMUTABLE, null /* options */, UserHandle.CURRENT);
}
public static CharSequence getVpnLabel(Context context, String packageName)
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 5948e7e..c762939 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -385,7 +385,6 @@
"/system/framework/android.hidl.manager-V1.0-java.jar", null /*packageName*/,
null /*codePaths*/, null /*name*/, 0 /*version*/, SharedLibraryInfo.TYPE_BUILTIN,
null /*declaringPackage*/, null /*dependentPackages*/, null /*dependencies*/);
- hidlManager.addDependency(hidlBase);
SharedLibraryInfo androidTestBase = new SharedLibraryInfo(
"/system/framework/android.test.base.jar", null /*packageName*/,
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 9257c6d..cd5502c 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -35,11 +35,13 @@
import android.view.inputmethod.InputConnectionInspector;
import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
import android.view.inputmethod.InputContentInfo;
+import android.view.inputmethod.SurroundingText;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.ICharSequenceResultCallback;
import com.android.internal.inputmethod.IExtractedTextResultCallback;
import com.android.internal.inputmethod.IIntResultCallback;
+import com.android.internal.inputmethod.ISurroundingTextResultCallback;
import com.android.internal.os.SomeArgs;
public abstract class IInputConnectionWrapper extends IInputContext.Stub {
@@ -70,6 +72,8 @@
private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140;
private static final int DO_CLOSE_CONNECTION = 150;
private static final int DO_COMMIT_CONTENT = 160;
+ private static final int DO_GET_SURROUNDING_TEXT = 41;
+
@GuardedBy("mLock")
@Nullable
@@ -127,6 +131,21 @@
dispatchMessage(mH.obtainMessage(DO_GET_SELECTED_TEXT, flags, 0 /* unused */, callback));
}
+ /**
+ * Dispatches the request for retrieving surrounding text.
+ *
+ * <p>See {@link InputConnection#getSurroundingText(int, int, int)}.
+ */
+ public void getSurroundingText(int beforeLength, int afterLength, int flags,
+ ISurroundingTextResultCallback callback) {
+ final SomeArgs args = SomeArgs.obtain();
+ args.arg1 = beforeLength;
+ args.arg2 = afterLength;
+ args.arg3 = flags;
+ args.arg4 = callback;
+ dispatchMessage(mH.obtainMessage(DO_GET_SURROUNDING_TEXT, flags, 0 /* unused */, args));
+ }
+
public void getCursorCapsMode(int reqModes, IIntResultCallback callback) {
dispatchMessage(
mH.obtainMessage(DO_GET_CURSOR_CAPS_MODE, reqModes, 0 /* unused */, callback));
@@ -293,6 +312,33 @@
}
return;
}
+ case DO_GET_SURROUNDING_TEXT: {
+ final SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ int beforeLength = (int) args.arg1;
+ int afterLength = (int) args.arg2;
+ int flags = (int) args.arg3;
+ final ISurroundingTextResultCallback callback =
+ (ISurroundingTextResultCallback) args.arg4;
+ final InputConnection ic = getInputConnection();
+ final SurroundingText result;
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "getSurroundingText on inactive InputConnection");
+ result = null;
+ } else {
+ result = ic.getSurroundingText(beforeLength, afterLength, flags);
+ }
+ try {
+ callback.onResult(result);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to return the result to getSurroundingText()."
+ + " result=" + result, e);
+ }
+ } finally {
+ args.recycle();
+ }
+ return;
+ }
case DO_GET_CURSOR_CAPS_MODE: {
final IIntResultCallback callback = (IIntResultCallback) msg.obj;
final InputConnection ic = getInputConnection();
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index 86f1293..074908a 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -26,6 +26,7 @@
import com.android.internal.inputmethod.ICharSequenceResultCallback;
import com.android.internal.inputmethod.IExtractedTextResultCallback;
import com.android.internal.inputmethod.IIntResultCallback;
+import com.android.internal.inputmethod.ISurroundingTextResultCallback;
/**
* Interface from an input method to the application, allowing it to perform
@@ -79,4 +80,7 @@
void commitContent(in InputContentInfo inputContentInfo, int flags, in Bundle opts,
IIntResultCallback callback);
+
+ void getSurroundingText(int beforeLength, int afterLength, int flags,
+ ISurroundingTextResultCallback callback);
}
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index 0bf5234..f086dd7 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -33,6 +33,7 @@
import android.view.inputmethod.InputConnectionInspector;
import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
import android.view.inputmethod.InputContentInfo;
+import android.view.inputmethod.SurroundingText;
import com.android.internal.inputmethod.CancellationGroup;
import com.android.internal.inputmethod.ResultCallbacks;
@@ -157,6 +158,38 @@
return getResultOrNull(value, "getSelectedText()");
}
+ /**
+ * Get {@link SurroundingText} around the current cursor, with <var>beforeLength</var>
+ * characters of text before the cursor, <var>afterLength</var> characters of text after the
+ * cursor, and all of the selected text.
+ * @param beforeLength The expected length of the text before the cursor
+ * @param afterLength The expected length of the text after the cursor
+ * @param flags Supplies additional options controlling how the text is returned. May be either
+ * 0 or {@link #GET_TEXT_WITH_STYLES}.
+ * @return the surrounding text around the cursor position; the length of the returned text
+ * might be less than requested. It could also be {@code null} when the editor or system could
+ * not support this protocol.
+ */
+ @AnyThread
+ public SurroundingText getSurroundingText(int beforeLength, int afterLength, int flags) {
+ if (mCancellationGroup.isCanceled()) {
+ return null;
+ }
+ if (isMethodMissing(MissingMethodFlags.GET_SURROUNDING_TEXT)) {
+ // This method is not implemented.
+ return null;
+ }
+ final CancellationGroup.Completable.SurroundingText value =
+ mCancellationGroup.createCompletableSurroundingText();
+ try {
+ mIInputContext.getSurroundingText(beforeLength, afterLength, flags,
+ ResultCallbacks.of(value));
+ } catch (RemoteException e) {
+ return null;
+ }
+ return getResultOrNull(value, "getSurroundingText()");
+ }
+
@AnyThread
public int getCursorCapsMode(int reqModes) {
if (mCancellationGroup.isCanceled()) {
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index 0fc0451..3e87cb5 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -53,9 +53,11 @@
queue->decStrong((void*)nativeCreate);
}
-static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr) {
+static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr,
+ jboolean includeSurfaceControlHandle) {
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
- return android_view_Surface_createFromSurface(env, queue->getSurface());
+ return android_view_Surface_createFromSurface(env,
+ queue->getSurface(includeSurfaceControlHandle));
}
static void nativeSetNextTransaction(JNIEnv* env, jclass clazz, jlong ptr, jlong transactionPtr) {
@@ -77,7 +79,7 @@
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{"nativeCreate", "(Ljava/lang/String;JJJZ)J", (void*)nativeCreate},
- {"nativeGetSurface", "(J)Landroid/view/Surface;", (void*)nativeGetSurface},
+ {"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface},
{"nativeDestroy", "(J)V", (void*)nativeDestroy},
{"nativeSetNextTransaction", "(JJ)V", (void*)nativeSetNextTransaction},
{"nativeUpdate", "(JJJJ)V", (void*)nativeUpdate},
diff --git a/core/jni/android_opengl_GLES10.cpp b/core/jni/android_opengl_GLES10.cpp
index e4d138d..d65b498 100644
--- a/core/jni/android_opengl_GLES10.cpp
+++ b/core/jni/android_opengl_GLES10.cpp
@@ -640,7 +640,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -684,7 +684,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -929,7 +929,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, indices, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)indices - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -2801,7 +2801,8 @@
exit:
if (_array) {
- releasePointer(_env, _array, pixels, _exception ? JNI_FALSE : JNI_TRUE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset),
+ _exception ? JNI_FALSE : JNI_TRUE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -3241,7 +3242,7 @@
(GLvoid *)pixels
);
if (_array) {
- releasePointer(_env, _array, pixels, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -3301,7 +3302,7 @@
(GLvoid *)pixels
);
if (_array) {
- releasePointer(_env, _array, pixels, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/jni/android_opengl_GLES11.cpp b/core/jni/android_opengl_GLES11.cpp
index 1069a1d..9724e6c 100644
--- a/core/jni/android_opengl_GLES11.cpp
+++ b/core/jni/android_opengl_GLES11.cpp
@@ -464,7 +464,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -509,7 +509,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/jni/android_opengl_GLES11Ext.cpp b/core/jni/android_opengl_GLES11Ext.cpp
index 86d7ecd..1ffa4ec 100644
--- a/core/jni/android_opengl_GLES11Ext.cpp
+++ b/core/jni/android_opengl_GLES11Ext.cpp
@@ -893,7 +893,8 @@
exit:
if (_array) {
- releasePointer(_env, _array, image, _exception ? JNI_FALSE : JNI_TRUE);
+ releasePointer(_env, _array, (void *)((char *)image - _bufferOffset),
+ _exception ? JNI_FALSE : JNI_TRUE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -930,7 +931,8 @@
exit:
if (_array) {
- releasePointer(_env, _array, image, _exception ? JNI_FALSE : JNI_TRUE);
+ releasePointer(_env, _array, (void *)((char *)image - _bufferOffset),
+ _exception ? JNI_FALSE : JNI_TRUE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/jni/android_opengl_GLES20.cpp b/core/jni/android_opengl_GLES20.cpp
index 49baa51..d832558 100644
--- a/core/jni/android_opengl_GLES20.cpp
+++ b/core/jni/android_opengl_GLES20.cpp
@@ -599,7 +599,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -644,7 +644,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -758,7 +758,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -802,7 +802,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -1379,7 +1379,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, indices, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)indices - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -4273,7 +4273,8 @@
exit:
if (_array) {
- releasePointer(_env, _array, pixels, _exception ? JNI_FALSE : JNI_TRUE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset),
+ _exception ? JNI_FALSE : JNI_TRUE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -4380,7 +4381,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, binary, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)binary - _bufferOffset), JNI_FALSE);
}
if (shaders_base) {
_env->ReleaseIntArrayElements(shaders_ref, (jint*)shaders_base,
@@ -4445,7 +4446,8 @@
exit:
if (_binaryArray) {
- releasePointer(_env, _binaryArray, binary, JNI_FALSE);
+ releasePointer(_env, _binaryArray, (void *)((char *)binary - _binaryBufferOffset),
+ JNI_FALSE);
}
if (_shadersArray) {
_env->ReleaseIntArrayElements(_shadersArray, (jint*)shaders, JNI_ABORT);
@@ -4568,7 +4570,7 @@
(GLvoid *)pixels
);
if (_array) {
- releasePointer(_env, _array, pixels, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -4816,7 +4818,7 @@
(GLvoid *)pixels
);
if (_array) {
- releasePointer(_env, _array, pixels, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/jni/android_opengl_GLES30.cpp b/core/jni/android_opengl_GLES30.cpp
index 32a2a24..719c6b3 100644
--- a/core/jni/android_opengl_GLES30.cpp
+++ b/core/jni/android_opengl_GLES30.cpp
@@ -463,7 +463,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, indices, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)indices - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -516,7 +516,7 @@
(GLvoid *)pixels
);
if (_array) {
- releasePointer(_env, _array, pixels, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -580,7 +580,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, pixels, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -660,7 +660,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -723,7 +723,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -5445,7 +5445,8 @@
exit:
if (_array) {
- releasePointer(_env, _array, binary, _exception ? JNI_FALSE : JNI_TRUE);
+ releasePointer(_env, _array, (void *)((char *)binary - _bufferOffset),
+ _exception ? JNI_FALSE : JNI_TRUE);
}
if (binaryFormat_base) {
_env->ReleaseIntArrayElements(binaryFormat_ref, (jint*)binaryFormat_base,
@@ -5519,7 +5520,8 @@
exit:
if (_binaryArray) {
- releasePointer(_env, _binaryArray, binary, _exception ? JNI_FALSE : JNI_TRUE);
+ releasePointer(_env, _binaryArray, (void *)((char *)binary - _binaryBufferOffset),
+ _exception ? JNI_FALSE : JNI_TRUE);
}
if (_binaryFormatArray) {
_env->ReleaseIntArrayElements(_binaryFormatArray, (jint*)binaryFormat, _exception ? JNI_ABORT : 0);
@@ -5564,7 +5566,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, binary, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)binary - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/jni/android_opengl_GLES32.cpp b/core/jni/android_opengl_GLES32.cpp
index 07a794d..7ed7548 100644
--- a/core/jni/android_opengl_GLES32.cpp
+++ b/core/jni/android_opengl_GLES32.cpp
@@ -863,7 +863,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, indices, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)indices - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -911,7 +911,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, indices, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)indices - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -1048,7 +1048,8 @@
exit:
if (_array) {
- releasePointer(_env, _array, data, _exception ? JNI_FALSE : JNI_TRUE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset),
+ _exception ? JNI_FALSE : JNI_TRUE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/jni/android_view_InputEventReceiver.md b/core/jni/android_view_InputEventReceiver.md
new file mode 100644
index 0000000..7df3461
--- /dev/null
+++ b/core/jni/android_view_InputEventReceiver.md
@@ -0,0 +1,49 @@
+# Batched consumption #
+
+Most apps draw once per vsync. Therefore, apps can only respond to 1 input event per frame. If multiple input events come in during the period of 1 vsync, it would be wasteful to deliver them all at once to the app. For this reason, input events are batched to only deliver 1 event per frame to the app.
+
+The batching process works in the following manner:
+
+1. `InputDispatcher` sends an event to the app
+2. The app's `Looper` is notified about the available event.
+3. The `handleEvent` callback is executed. Events are read from fd.
+4. If a batched input event is available, `InputConsumer::hasPendingBatch` returns true. No event is sent to the app at this point.
+5. The app is notified that a batched event is available for consumption, and schedules a runnable via the `Choreographer` to consume it a short time before the next frame
+6. When the scheduled runnable is executed, it doesn't just consume the batched input. It proactively tries to consume everything that has come in to the socket.
+7. The batched event is sent to the app, along with any of the other events that have come in.
+
+Let's discuss the specifics of some of these steps.
+
+## 1. Consuming events in `handleEvent` callback ##
+
+The app is notified about the available event via the `Looper` callback `handleEvent`. When the app's input socket becomes readable (e.g., it has unread events), the looper will execute `handleEvent`. At this point, the app is expected to read in the events that have come in to the socket. The function `handleEvent` will continue to trigger as long as there are unread events in the socket. Thus, the app could choose to read events 1 at a time, or all at once. If there are no more events in the app's socket, handleEvent will no longer execute.
+
+Even though it is perfectly valid for the app to read events 1 at a time, it is more efficient to read them all at once. Therefore, whenever the events are available, the app will try to completely drain the socket.
+
+To consume the events inside `handleEvent`, the app calls `InputConsumer::consume(.., consumeBatches=false, frameTime=-1, ..)`. That is, when `handleEvent` runs, there is no information about the upcoming frameTime, and we dont want to consume the batches because there may be other events that come in before the 'consume batched input' runnable runs.
+
+If a batched event comes in at this point (typically, any MOVE event that has source = TOUCHSCREEN), the `consume` function above would actually return a `NULL` event with status `WOULD_BLOCK`. When this happens, the caller (`NativeInputEventReceiver`) is responsible for checking whether `InputConsumer::hasPendingBatch` is set to true. If so, the caller is responsible for scheduling a runnable to consume these batched events.
+
+## 2. Consuming batched events ##
+
+In the previous section, we learned that the app can read events inside the `handleEvent` callback. The other time when the app reads events is when the 'consume batched input' runnable is executed. This runnable is scheduled via the Choreographer by requesting a `CALLBACK_INPUT` event.
+
+Before the batched events are consumed, the socket is drained once again. This is an optimization.
+
+To consume the events inside 'consume batched input' runnable, the app calls `InputConsumer::consume(.., consumeBatches=true, frameTime=<valid frame time>, ..)`. At this point, the `consume` function will return all batched events up to the `frameTime` point. There may be batched events remaining.
+
+## 3. Key points ##
+
+Some of the behaviours above should be highlighted, because they may be unexpected.
+
+1. Even if events have been read by `InputConsumer`, `consume` will return `NULL` event with status `WOULD_BLOCK` if those events caused a new batch to be started.
+
+2. Events are read from the fd outside of the regular `handleEvent` case, during batched consumption.
+
+3. The function `handleEvent` will always execute as long as there are unread events in the fd
+
+4. The `consume` function is called in 1 of 2 possible ways:
+ - `consumeBatches=false, frameTime=-1`
+ - `consumeBatches=true, frameTime=<valid time>`
+
+ I.e., it is never called with `consumeBatches=true, frameTime=-1`.
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 5c87f19..3a1ccd9 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -313,7 +313,7 @@
return nativeObject;
}
- sp<Surface> surface = queue->getSurface();
+ sp<Surface> surface = queue->getSurface(true /* includeSurfaceControlHandle */);
if (surface != NULL) {
surface->incStrong(&sRefBaseOwner);
}
@@ -349,7 +349,8 @@
sp<Surface> sur;
if (surfaceShim.graphicBufferProducer != nullptr) {
// we have a new IGraphicBufferProducer, create a new Surface for it
- sur = new Surface(surfaceShim.graphicBufferProducer, true);
+ sur = new Surface(surfaceShim.graphicBufferProducer, true,
+ surfaceShim.surfaceControlHandle);
// and keep a reference before passing to java
sur->incStrong(&sRefBaseOwner);
}
@@ -373,6 +374,7 @@
android::view::Surface surfaceShim;
if (self != nullptr) {
surfaceShim.graphicBufferProducer = self->getIGraphicBufferProducer();
+ surfaceShim.surfaceControlHandle = self->getSurfaceControlHandle();
}
// Calling code in Surface.java has already written the name of the Surface
// to the Parcel
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index a61903d..62f844e 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -318,8 +318,13 @@
}
}
- status_t err = client->createSurfaceChecked(
- String8(name.c_str()), w, h, format, &surface, flags, parent, std::move(metadata));
+ sp<IBinder> parentHandle;
+ if (parent != nullptr) {
+ parentHandle = parent->getHandle();
+ }
+
+ status_t err = client->createSurfaceChecked(String8(name.c_str()), w, h, format, &surface,
+ flags, parentHandle, std::move(metadata));
if (err == NAME_NOT_FOUND) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return 0;
diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp
index ffc1ddc..21de723 100644
--- a/core/jni/com_google_android_gles_jni_GLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp
@@ -424,7 +424,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -468,7 +468,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -713,7 +713,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, indices, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)indices - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -3488,7 +3488,8 @@
exit:
if (_array) {
- releasePointer(_env, _array, pixels, _exception ? JNI_FALSE : JNI_TRUE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset),
+ _exception ? JNI_FALSE : JNI_TRUE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -3972,7 +3973,7 @@
(GLvoid *)pixels
);
if (_array) {
- releasePointer(_env, _array, pixels, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -4032,7 +4033,7 @@
(GLvoid *)pixels
);
if (_array) {
- releasePointer(_env, _array, pixels, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)pixels - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -4299,7 +4300,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
@@ -4344,7 +4345,7 @@
exit:
if (_array) {
- releasePointer(_env, _array, data, JNI_FALSE);
+ releasePointer(_env, _array, (void *)((char *)data - _bufferOffset), JNI_FALSE);
}
if (_exception) {
jniThrowException(_env, _exceptionType, _exceptionMessage);
diff --git a/core/proto/android/app/enums.proto b/core/proto/android/app/enums.proto
index bd5cb62..2d2c8ac 100644
--- a/core/proto/android/app/enums.proto
+++ b/core/proto/android/app/enums.proto
@@ -207,4 +207,8 @@
APP_OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97;
APP_OP_AUTO_REVOKE_MANAGED_BY_INSTALLER = 98;
APP_OP_NO_ISOLATED_STORAGE = 99;
+ APP_OP_PHONE_CALL_MICROPHONE = 100;
+ APP_OP_PHONE_CALL_CAMERA = 101;
+ APP_OP_RECORD_AUDIO_HOTWORD = 102;
+ APP_OP_MANAGE_ONGOING_CALLS = 103;
}
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 99fe1af..45f64f9 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2761,4 +2761,9 @@
// CATEGORY: SETTINGS
// OS: S
REDUCE_BRIGHT_COLORS_SETTINGS = 1853;
+
+ // OPEN: Settings > Location > Time Zone Detection
+ // CATEGORY: SETTINGS
+ // OS: S
+ LOCATION_TIME_ZONE_DETECTION = 1854;
}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 9fed1b9..fe65bda3 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -511,9 +511,14 @@
(section).args = "sensorservice --proto"
];
- optional com.android.server.powerstats.PowerStatsServiceProto powerstats = 3054 [
+ optional com.android.server.powerstats.PowerStatsServiceMeterProto powerstats_meter = 3054 [
(section).type = SECTION_DUMPSYS,
- (section).args = "power_stats --proto"
+ (section).args = "power_stats --proto meter"
+ ];
+
+ optional com.android.server.powerstats.PowerStatsServiceModelProto powerstats_model = 3055 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "power_stats --proto model"
];
// Dumps in text format (on userdebug and eng builds only): 4000 ~ 4999
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 16a691c..9291a90 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -510,7 +510,7 @@
optional IntentFirewall intent_firewall = 65;
reserved 66; // job_scheduler_constants
- optional SettingProto job_scheduler_quota_controller_constants = 149 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ reserved 149; // job_scheduler_quota_controller_constants
reserved 150; // job_scheduler_time_controller_constants
optional SettingProto keep_profile_in_background = 67 [ (android.privacy).dest = DEST_AUTOMATIC ];
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 83c5391..d934b82 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -219,8 +219,10 @@
optional SettingProto emergency_assistance_application = 22 [ (android.privacy).dest = DEST_AUTOMATIC ];
message EmergencyResponse {
- optional SettingProto panic_gesture_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
- optional SettingProto panic_sound_enabled = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto emergency_gesture_enabled = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto emergency_gesture_sound_enabled = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+ reserved 1,2;
}
optional EmergencyResponse emergency_response = 83;
diff --git a/core/proto/android/server/fingerprint.proto b/core/proto/android/server/fingerprint.proto
index a264f18..a49a1ad 100644
--- a/core/proto/android/server/fingerprint.proto
+++ b/core/proto/android/server/fingerprint.proto
@@ -66,3 +66,36 @@
// Total number of permanent lockouts.
optional int32 permanent_lockout = 5;
}
+
+// Internal FingerprintService states. The above messages (FingerprintServiceDumpProto, etc)
+// are used for legacy metrics and should not be modified.
+message FingerprintServiceStateProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ repeated SensorStateProto sensor_states = 1;
+}
+
+// State of a single sensor.
+message SensorStateProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // Unique sensorId
+ optional int32 sensor_id = 1;
+
+ // State of the sensor's scheduler. True if currently handling an operation, false if idle.
+ optional bool is_busy = 2;
+
+ // User states for this sensor.
+ repeated UserStateProto user_states = 3;
+}
+
+// State of a specific user for a specific sensor.
+message UserStateProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // Android user ID
+ optional int32 user_id = 1;
+
+ // Number of fingerprints enrolled
+ optional int32 num_enrolled = 2;
+}
\ No newline at end of file
diff --git a/core/proto/android/server/powerstatsservice.proto b/core/proto/android/server/powerstatsservice.proto
index c805244..9a7ed7c 100644
--- a/core/proto/android/server/powerstatsservice.proto
+++ b/core/proto/android/server/powerstatsservice.proto
@@ -20,44 +20,99 @@
option java_multiple_files = true;
-message IncidentReportProto {
+/**
+ * IncidentReportMeterProto is used only in the parsing tool located
+ * in frameworks/base/tools which is used to parse this data out of
+ * incident reports.
+ */
+message IncidentReportMeterProto {
/** Section number matches that in incident.proto */
- optional PowerStatsServiceProto incident_report = 3054;
-}
-
-message PowerStatsServiceProto {
- repeated RailInfoProto rail_info = 1;
- repeated EnergyDataProto energy_data = 2;
+ optional PowerStatsServiceMeterProto incident_report = 3054;
}
/**
- * Rail information:
- * Reports information related to the rails being monitored.
+ * IncidentReportModelProto is used only in the parsing tool located
+ * in frameworks/base/tools which is used to parse this data out of
+ * incident reports.
*/
-message RailInfoProto {
- /** Index corresponding to the rail */
- optional int32 index = 1;
-
- /** Name of the rail (opaque to the framework) */
- optional string rail_name = 2;
-
- /** Name of the subsystem to which this rail belongs (opaque to the framework) */
- optional string subsys_name = 3;
-
- /** Hardware sampling rate */
- optional int32 sampling_rate = 4;
+message IncidentReportModelProto {
+ /** Section number matches that in incident.proto */
+ optional PowerStatsServiceModelProto incident_report = 3055;
}
/**
- * Rail level energy measurements:
- * Reports accumulated energy since boot on each rail.
+ * EnergyConsumer (model) data is exposed by the PowerStats HAL. This data
+ * represents modeled energy consumption estimates and is provided per
+ * subsystem. The default subsystems are defined in EnergyConsumerId.aidl.
+ * Energy model estimates will be logged to incident reports in addition to
+ * the raw energy meter data.
*/
-message EnergyDataProto {
+message PowerStatsServiceModelProto {
+ repeated EnergyConsumerIdProto energy_consumer_id = 1;
+ repeated EnergyConsumerResultProto energy_consumer_result = 2;
+}
+
+/**
+ * EnergyMeasurement (meter) data is exposed by the PowerStats HAL. This data
+ * represents measurements taken directly from on-device energy meters.
+ * This raw energy meter data will be logged to incident reports.
+ */
+message PowerStatsServiceMeterProto {
+ repeated ChannelInfoProto channel_info = 1;
+ repeated EnergyMeasurementProto energy_measurement = 2;
+}
+
+/**
+ * Energy consumer ID:
+ * A list of default subsystems for which energy consumption estimates
+ * may be provided (hardware dependent).
+ */
+message EnergyConsumerIdProto {
+ /** Unique index identifying the energy consumer. */
+ optional int32 energy_consumer_id = 1;
+}
+
+/**
+ * Energy consumer result:
+ * An estimate of energy consumption since boot for the subsystem identified
+ * by the unique energy_consumer_id.
+ */
+message EnergyConsumerResultProto {
+ /** Unique index identifying the energy consumer. */
+ optional int32 energy_consumer_id = 1;
+
+ /** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */
+ optional int64 timestamp_ms = 2;
+
+ /** Accumulated energy since device boot in microwatt-seconds (uWs) */
+ optional int64 energy_uws = 3;
+}
+
+/**
+ * Channel information:
+ * Reports information related to the energy meter channels being monitored.
+ */
+message ChannelInfoProto {
/**
- * Index corresponding to the rail. This index matches
- * the index returned in RailInfo
+ * Index corresponding to the energy meter channel. This index matches
+ * the index returned in ChannelInfo.
*/
- optional int32 index = 1;
+ optional int32 channel_id = 1;
+
+ /** Name of the energy meter channel */
+ optional string channel_name = 2;
+}
+
+/**
+ * Energy measurements:
+ * Reports accumulated energy since boot for each energy meter.
+ */
+message EnergyMeasurementProto {
+ /**
+ * Index corresponding to the energy meter channel. This index matches
+ * the index returned in ChannelInfo.
+ */
+ optional int32 channel_id = 1;
/** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */
optional int64 timestamp_ms = 2;
diff --git a/core/proto/android/stats/style/style_enums.proto b/core/proto/android/stats/style/style_enums.proto
index 828e412..2876882 100644
--- a/core/proto/android/stats/style/style_enums.proto
+++ b/core/proto/android/stats/style/style_enums.proto
@@ -41,6 +41,7 @@
LIVE_WALLPAPER_QUESTIONNAIRE_SELECT = 19;
LIVE_WALLPAPER_QUESTIONNAIRE_APPLIED = 20;
LIVE_WALLPAPER_EFFECT_SHOW = 21;
+ APP_LAUNCHED = 22;
}
enum LocationPreference {
@@ -55,3 +56,14 @@
DATE_UNAVAILABLE = 1;
DATE_MANUAL = 2;
}
+
+enum LaunchedPreference {
+ LAUNCHED_PREFERENCE_UNSPECIFIED = 0;
+ LAUNCHED_LAUNCHER = 1;
+ LAUNCHED_SETTINGS = 2;
+ LAUNCHED_SUW = 3;
+ LAUNCHED_TIPS = 4;
+ LAUNCHED_LAUNCH_ICON = 5;
+ LAUNCHED_CROP_AND_SET_ACTION = 6;
+ LAUNCHED_DEEP_LINK = 7;
+}
diff --git a/core/proto/android/stats/tls/enums.proto b/core/proto/android/stats/tls/enums.proto
new file mode 100644
index 0000000..0ae87ee
--- /dev/null
+++ b/core/proto/android/stats/tls/enums.proto
@@ -0,0 +1,70 @@
+/*
+ * 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.stats.tls;
+
+// Keep in sync with
+// external/conscrypt/{android,platform}/src/main/java/org/conscrypt/Platform.java
+enum Protocol {
+ UNKNOWN_PROTO = 0;
+ SSLv3 = 1;
+ TLSv1 = 2;
+ TLSv1_1 = 3;
+ TLSv1_2 = 4;
+ TLSv1_3 = 5;
+}
+
+// Cipher suites' ids are based on IANA's database:
+// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4
+//
+// If you add new cipher suite, make sure id is the same as in IANA's database (see link above)
+//
+// Keep in sync with
+// external/conscrypt/{android,platform}/src/main/java/org/conscrypt/Platform.java
+enum CipherSuite {
+ UNKNOWN_CIPHER_SUITE = 0x0000;
+
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A;
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014;
+ TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035;
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009;
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013;
+ TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F;
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A;
+
+ // TLSv1.2 cipher suites
+ TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C;
+ TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D;
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F;
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030;
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B;
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C;
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA9;
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA8;
+
+ // Pre-Shared Key (PSK) cipher suites
+ TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C;
+ TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D;
+ TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = 0xC035;
+ TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = 0xC036;
+ TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAC;
+
+ // TLS 1.3 cipher suites
+ TLS_AES_128_GCM_SHA256 = 0x1301;
+ TLS_AES_256_GCM_SHA384 = 0x1302;
+ TLS_CHACHA20_POLY1305_SHA256 = 0x1303;
+}
+
diff --git a/core/proto/android/telephony/enums.proto b/core/proto/android/telephony/enums.proto
index d2c0276..2546b51 100644
--- a/core/proto/android/telephony/enums.proto
+++ b/core/proto/android/telephony/enums.proto
@@ -232,13 +232,13 @@
// Data profile of the data call. From
// frameworks/base/telephony/java/com/android/internal/telephony/RILConstants.java
enum DataProfileEnum {
- DATA_PROFILE_INVALID = -1;
DATA_PROFILE_DEFAULT = 0;
DATA_PROFILE_TETHERED = 1;
DATA_PROFILE_IMS = 2;
DATA_PROFILE_FOTA = 3;
DATA_PROFILE_CBS = 4;
DATA_PROFILE_OEM_BASE = 1000;
+ DATA_PROFILE_INVALID = -1;
}
// Reason of data call deactivation. From
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0195451..46956f0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -349,6 +349,7 @@
<protected-broadcast android:name="com.android.server.WifiManager.action.START_PNO" />
<protected-broadcast android:name="com.android.server.WifiManager.action.DELAYED_DRIVER_STOP" />
<protected-broadcast android:name="com.android.server.WifiManager.action.DEVICE_IDLE" />
+ <protected-broadcast android:name="com.android.server.action.REMOTE_BUGREPORT_DISPATCH" />
<protected-broadcast android:name="com.android.server.action.REMOTE_BUGREPORT_SHARING_ACCEPTED" />
<protected-broadcast android:name="com.android.server.action.REMOTE_BUGREPORT_SHARING_DECLINED" />
<protected-broadcast android:name="com.android.internal.action.EUICC_FACTORY_RESET" />
@@ -668,6 +669,10 @@
<!-- For tether entitlement recheck-->
<protected-broadcast
android:name="com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM" />
+
+ <!-- Made protected in S (was added in R) -->
+ <protected-broadcast android:name="com.android.internal.intent.action.BUGREPORT_REQUESTED" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -2229,6 +2234,11 @@
<permission android:name="android.permission.BIND_INCALL_SERVICE"
android:protectionLevel="signature|privileged" />
+ <!-- Allows to query ongoing call details and manage ongoing calls
+ <p>Protection level: signature|appop -->
+ <permission android:name="android.permission.MANAGE_ONGOING_CALLS"
+ android:protectionLevel="signature|appop" />
+
<!-- Allows the app to request network scans from telephony.
<p>Not for use by third-party applications.
@SystemApi @hide-->
@@ -5150,6 +5160,12 @@
<permission android:name="android.permission.INPUT_CONSUMER"
android:protectionLevel="signature" />
+ <!-- @hide Allows an application to control the system's device state managed by the
+ {@link android.service.devicestate.DeviceStateManagerService}. For example, on foldable
+ devices this would grant access to toggle between the folded and unfolded states. -->
+ <permission android:name="android.permission.CONTROL_DEVICE_STATE"
+ android:protectionLevel="signature" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 88493c9..d11875e 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -38,81 +38,7 @@
android:layout_gravity="center"
/>
</FrameLayout>
- <TextView
- android:id="@+id/app_name_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
- android:layout_marginStart="@dimen/notification_header_app_name_margin_start"
- android:layout_marginEnd="@dimen/notification_header_separating_margin"
- android:visibility="?attr/notificationHeaderAppNameVisibility"
- android:singleLine="true"
- />
- <TextView
- android:id="@+id/header_text_secondary_divider"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
- android:layout_marginStart="@dimen/notification_header_separating_margin"
- android:layout_marginEnd="@dimen/notification_header_separating_margin"
- android:text="@string/notification_header_divider_symbol"
- android:visibility="gone"/>
- <TextView
- android:id="@+id/header_text_secondary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
- android:layout_marginStart="@dimen/notification_header_separating_margin"
- android:layout_marginEnd="@dimen/notification_header_separating_margin"
- android:visibility="gone"
- android:singleLine="true"/>
- <TextView
- android:id="@+id/header_text_divider"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
- android:layout_marginStart="@dimen/notification_header_separating_margin"
- android:layout_marginEnd="@dimen/notification_header_separating_margin"
- android:text="@string/notification_header_divider_symbol"
- android:visibility="gone"/>
- <TextView
- android:id="@+id/header_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
- android:layout_marginStart="@dimen/notification_header_separating_margin"
- android:layout_marginEnd="@dimen/notification_header_separating_margin"
- android:visibility="gone"
- android:singleLine="true"/>
- <TextView
- android:id="@+id/time_divider"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?attr/notificationHeaderTextAppearance"
- android:layout_marginStart="@dimen/notification_header_separating_margin"
- android:layout_marginEnd="@dimen/notification_header_separating_margin"
- android:text="@string/notification_header_divider_symbol"
- android:singleLine="true"
- android:visibility="gone"/>
- <DateTimeView
- android:id="@+id/time"
- android:textAppearance="@style/TextAppearance.Material.Notification.Time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/notification_header_separating_margin"
- android:layout_marginEnd="@dimen/notification_header_separating_margin"
- android:showRelative="true"
- android:singleLine="true"
- android:visibility="gone" />
- <ViewStub
- android:id="@+id/chronometer"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/notification_header_separating_margin"
- android:layout_marginEnd="@dimen/notification_header_separating_margin"
- android:layout="@layout/notification_template_part_chronometer"
- android:visibility="gone"
- />
+ <include layout="@layout/notification_template_top_line" />
<com.android.internal.widget.NotificationExpandButton
android:id="@+id/expand_button"
android:background="@null"
@@ -123,42 +49,5 @@
android:visibility="gone"
android:contentDescription="@string/expand_button_content_description_collapsed"
/>
- <ImageView
- android:id="@+id/alerted_icon"
- android:layout_width="@dimen/notification_alerted_size"
- android:layout_height="@dimen/notification_alerted_size"
- android:layout_gravity="center"
- android:layout_marginStart="4dp"
- android:paddingTop="1dp"
- android:scaleType="fitCenter"
- android:visibility="gone"
- android:contentDescription="@string/notification_alerted_content_description"
- android:src="@drawable/ic_notifications_alerted"
- />
- <ImageButton
- android:id="@+id/feedback"
- android:layout_width="@dimen/notification_feedback_size"
- android:layout_height="@dimen/notification_feedback_size"
- android:layout_marginStart="6dp"
- android:layout_marginEnd="6dp"
- android:paddingTop="2dp"
- android:layout_gravity="center"
- android:scaleType="fitCenter"
- android:src="@drawable/ic_feedback_indicator"
- android:background="?android:selectableItemBackgroundBorderless"
- android:visibility="gone"
- android:contentDescription="@string/notification_feedback_indicator"
- />
- <ImageView
- android:id="@+id/profile_badge"
- android:layout_width="@dimen/notification_badge_size"
- android:layout_height="@dimen/notification_badge_size"
- android:layout_gravity="center"
- android:layout_marginStart="4dp"
- android:paddingTop="1dp"
- android:scaleType="fitCenter"
- android:visibility="gone"
- android:contentDescription="@string/notification_work_profile_content_description"
- />
</NotificationHeaderView>
diff --git a/core/res/res/layout/notification_template_top_line.xml b/core/res/res/layout/notification_template_top_line.xml
new file mode 100644
index 0000000..27fab85
--- /dev/null
+++ b/core/res/res/layout/notification_template_top_line.xml
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<!-- extends ViewGroup -->
+<NotificationTopLineView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ android:id="@+id/notification_top_line"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/notification_header_height"
+ android:clipChildren="false"
+ >
+ <TextView
+ android:id="@+id/app_name_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:layout_marginStart="@dimen/notification_header_app_name_margin_start"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:visibility="?attr/notificationHeaderAppNameVisibility"
+ android:singleLine="true"
+ />
+ <TextView
+ android:id="@+id/header_text_secondary_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:text="@string/notification_header_divider_symbol"
+ android:visibility="gone"/>
+ <TextView
+ android:id="@+id/header_text_secondary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:visibility="gone"
+ android:singleLine="true"/>
+ <TextView
+ android:id="@+id/header_text_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:text="@string/notification_header_divider_symbol"
+ android:visibility="gone"/>
+ <TextView
+ android:id="@+id/header_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:visibility="gone"
+ android:singleLine="true"/>
+ <TextView
+ android:id="@+id/time_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:text="@string/notification_header_divider_symbol"
+ android:singleLine="true"
+ android:visibility="gone"/>
+ <DateTimeView
+ android:id="@+id/time"
+ android:textAppearance="@style/TextAppearance.Material.Notification.Time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:showRelative="true"
+ android:singleLine="true"
+ android:visibility="gone" />
+ <ViewStub
+ android:id="@+id/chronometer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:layout="@layout/notification_template_part_chronometer"
+ android:visibility="gone"
+ />
+ <ImageView
+ android:id="@+id/alerted_icon"
+ android:layout_width="@dimen/notification_alerted_size"
+ android:layout_height="@dimen/notification_alerted_size"
+ android:layout_gravity="center"
+ android:layout_marginStart="4dp"
+ android:paddingTop="1dp"
+ android:scaleType="fitCenter"
+ android:visibility="gone"
+ android:contentDescription="@string/notification_alerted_content_description"
+ android:src="@drawable/ic_notifications_alerted"
+ />
+ <ImageButton
+ android:id="@+id/feedback"
+ android:layout_width="@dimen/notification_feedback_size"
+ android:layout_height="@dimen/notification_feedback_size"
+ android:layout_marginStart="6dp"
+ android:layout_marginEnd="6dp"
+ android:paddingTop="2dp"
+ android:layout_gravity="center"
+ android:scaleType="fitCenter"
+ android:src="@drawable/ic_feedback_indicator"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:visibility="gone"
+ android:contentDescription="@string/notification_feedback_indicator"
+ />
+ <ImageView
+ android:id="@+id/profile_badge"
+ android:layout_width="@dimen/notification_badge_size"
+ android:layout_height="@dimen/notification_badge_size"
+ android:layout_gravity="center"
+ android:layout_marginStart="4dp"
+ android:paddingTop="1dp"
+ android:scaleType="fitCenter"
+ android:visibility="gone"
+ android:contentDescription="@string/notification_work_profile_content_description"
+ />
+</NotificationTopLineView>
+
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index bd81519..747ecb35 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"AF"</string>
<string name="checked" msgid="9179896827054513119">"gemerk"</string>
<string name="not_checked" msgid="7972320087569023342">"nie gemerk nie"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Voltooi handeling met"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Voltooi handeling met gebruik van %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Voltooi handeling"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 0bbe232..4142a5c 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"ውጪ"</string>
<string name="checked" msgid="9179896827054513119">"ምልክት ተደርጎበታል"</string>
<string name="not_checked" msgid="7972320087569023342">"ምልክት አልተደረገበትም"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"... በመጠቀም ድርጊቱን አጠናቅ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$sን ተጠቅመው እርምጃ ያጠናቅቁ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"እርምጃውን አጠናቅቅ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 8868fb6..8ec3140 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1209,6 +1209,10 @@
<string name="capital_off" msgid="7443704171014626777">"إيقاف"</string>
<string name="checked" msgid="9179896827054513119">"تم وضع علامة"</string>
<string name="not_checked" msgid="7972320087569023342">"لم يتم وضع علامة"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"إكمال الإجراء باستخدام"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"إكمال الإجراء باستخدام %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"إكمال الإجراء"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 619666b..d142b6b 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"অফ কৰক"</string>
<string name="checked" msgid="9179896827054513119">"টিক চিহ্ন দিয়া হৈছে"</string>
<string name="not_checked" msgid="7972320087569023342">"টিক চিহ্ন দিয়া হোৱা নাই"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"এয়া ব্যৱহাৰ কৰি কার্য সম্পূর্ণ কৰক"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ব্যৱহাৰ কৰি কাৰ্যটো সম্পূৰ্ণ কৰক"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"কাৰ্য সম্পূৰ্ণ কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index ed708cc..0db22cc 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"QAPALI"</string>
<string name="checked" msgid="9179896827054513119">"yoxlanılıb"</string>
<string name="not_checked" msgid="7972320087569023342">"yoxlanılmayıb"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Əməliyyatı tamamlayın:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s istifadə edərək əməliyyatı tamamlayın"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Əməliyyatı tamamlayın"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 0b21545..a210902 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -94,7 +94,7 @@
<string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"Status mobilnih podataka"</string>
<string name="notification_channel_sms" msgid="1243384981025535724">"SMS-ovi"</string>
<string name="notification_channel_voice_mail" msgid="8457433203106654172">"Poruke govorne pošte"</string>
- <string name="notification_channel_wfc" msgid="9048240466765169038">"Pozivanje preko Wi-Fi mreže"</string>
+ <string name="notification_channel_wfc" msgid="9048240466765169038">"Pozivanje preko WiFi mreže"</string>
<string name="notification_channel_sim" msgid="5098802350325677490">"Status SIM-a"</string>
<string name="notification_channel_sim_high_prio" msgid="642361929452850928">"Obaveštenja SIM kartice sa statusom „visok prioritet“"</string>
<string name="peerTtyModeFull" msgid="337553730440832160">"Korisnik zahteva POTPUN režim TTY"</string>
@@ -123,30 +123,30 @@
<string name="roamingText11" msgid="5245687407203281407">"Baner rominga je uključen"</string>
<string name="roamingText12" msgid="673537506362152640">"Baner rominga je isključen"</string>
<string name="roamingTextSearching" msgid="5323235489657753486">"Pretraživanje usluge"</string>
- <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Podešavanje pozivanja preko Wi-Fi-ja nije uspelo"</string>
+ <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Podešavanje pozivanja preko WiFi-ja nije uspelo"</string>
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="468830943567116703">"Da biste upućivali pozive i slali poruke preko Wi-Fi-ja, prvo zatražite od mobilnog operatera da vam omogući ovu uslugu. Zatim u Podešavanjima ponovo uključite Pozivanje preko Wi-Fi-ja. (kôd greške: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
+ <item msgid="468830943567116703">"Da biste upućivali pozive i slali poruke preko WiFi-ja, prvo zatražite od mobilnog operatera da vam omogući ovu uslugu. Zatim u Podešavanjima ponovo uključite Pozivanje preko WiFi-ja. (kôd greške: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="4795145070505729156">"Problem u vezi sa registrovanjem pozivanja preko Wi‑Fi-ja kod mobilnog operatera: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
<!-- no translation found for wfcSpnFormat_spn (2982505428519096311) -->
<skip />
- <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"<xliff:g id="SPN">%s</xliff:g> pozivanje preko Wi-Fi-ja"</string>
- <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"<xliff:g id="SPN">%s</xliff:g> – pozivanje preko Wi-Fi-ja"</string>
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"<xliff:g id="SPN">%s</xliff:g> pozivanje preko WiFi-ja"</string>
+ <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"<xliff:g id="SPN">%s</xliff:g> – pozivanje preko WiFi-ja"</string>
<string name="wfcSpnFormat_wlan_call" msgid="4895315549916165700">"WLAN poziv"</string>
<string name="wfcSpnFormat_spn_wlan_call" msgid="255919245825481510">"<xliff:g id="SPN">%s</xliff:g> WLAN poziv"</string>
- <string name="wfcSpnFormat_spn_wifi" msgid="7232899594327126970">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
- <string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="8383917598312067365">"Pozivanje preko Wi-Fi-ja | <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_spn_wifi" msgid="7232899594327126970">"<xliff:g id="SPN">%s</xliff:g> WiFi"</string>
+ <string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="8383917598312067365">"Pozivanje preko WiFi-ja | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="6865214948822061486">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Pozivanje preko Wi-Fi-ja"</string>
- <string name="wfcSpnFormat_wifi" msgid="1376356951297043426">"Wi-Fi"</string>
- <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Pozivanje preko Wi-Fi-ja"</string>
+ <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Pozivanje preko WiFi-ja"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1376356951297043426">"WiFi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Pozivanje preko WiFi-ja"</string>
<string name="wfcSpnFormat_vowifi" msgid="8371335230890725606">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="5626710010766902560">"Isključeno"</string>
- <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Pozivanje preko Wi-Fi-ja"</string>
+ <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Pozivanje preko WiFi-ja"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Poziv preko mobilne mreže"</string>
- <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Samo Wi-Fi"</string>
+ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Samo WiFi"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nije prosleđeno"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> nakon <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunde/i"</string>
@@ -501,14 +501,14 @@
<string name="permdesc_changeNetworkState" msgid="649341947816898736">"Dozvoljava aplikaciji da menja status povezivanja sa mrežom."</string>
<string name="permlab_changeTetherState" msgid="9079611809931863861">"promena povezivanja privezivanjem"</string>
<string name="permdesc_changeTetherState" msgid="3025129606422533085">"Dozvoljava aplikaciji da menja status veze sa privezanom mrežom."</string>
- <string name="permlab_accessWifiState" msgid="5552488500317911052">"pregled Wi-Fi veza"</string>
- <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Dozvoljava aplikaciji da pregleda informacije o Wi-Fi umrežavanju, kao što su informacije o tome da li je Wi-Fi omogućen i nazivi povezanih Wi-Fi uređaja."</string>
- <string name="permlab_changeWifiState" msgid="7947824109713181554">"povezivanje i prekid veze sa Wi-Fi mrežom"</string>
- <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Dozvoljava aplikaciji da se povezuje sa pristupnim tačkama za Wi-Fi i prekida vezu sa njima, kao i da unosi promene u konfiguraciju uređaja za Wi-Fi mreže."</string>
- <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"omogućavanje prijema višesmernog Wi-Fi saobraćaja"</string>
- <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Dozvoljava aplikaciji da prima pakete koji se šalju na sve uređaje na Wi-Fi mreži pomoću višesmernih adresa, a ne samo na tablet. Koristi više napajanja od režima jednosmernog saobraćaja."</string>
- <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Dozvoljava aplikaciji da prima pakete koji se šalju na sve uređaje na Wi-Fi mreži pomoću višesmernih adresa, a ne samo na Android TV uređaj. Koristi više energije od režima bez višesmernog slanja."</string>
- <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Dozvoljava aplikaciji da prima pakete koji se šalju na sve uređaje na Wi-Fi mreži pomoću višesmernih adresa, a ne samo na telefon. Koristi više napajanja od režima jednosmernog saobraćaja."</string>
+ <string name="permlab_accessWifiState" msgid="5552488500317911052">"pregled WiFi veza"</string>
+ <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Dozvoljava aplikaciji da pregleda informacije o WiFi umrežavanju, kao što su informacije o tome da li je WiFi omogućen i nazivi povezanih WiFi uređaja."</string>
+ <string name="permlab_changeWifiState" msgid="7947824109713181554">"povezivanje i prekid veze sa WiFi mrežom"</string>
+ <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Dozvoljava aplikaciji da se povezuje sa pristupnim tačkama za WiFi i prekida vezu sa njima, kao i da unosi promene u konfiguraciju uređaja za WiFi mreže."</string>
+ <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"omogućavanje prijema višesmernog WiFi saobraćaja"</string>
+ <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Dozvoljava aplikaciji da prima pakete koji se šalju na sve uređaje na WiFi mreži pomoću višesmernih adresa, a ne samo na tablet. Koristi više napajanja od režima jednosmernog saobraćaja."</string>
+ <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Dozvoljava aplikaciji da prima pakete koji se šalju na sve uređaje na WiFi mreži pomoću višesmernih adresa, a ne samo na Android TV uređaj. Koristi više energije od režima bez višesmernog slanja."</string>
+ <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Dozvoljava aplikaciji da prima pakete koji se šalju na sve uređaje na WiFi mreži pomoću višesmernih adresa, a ne samo na telefon. Koristi više napajanja od režima jednosmernog saobraćaja."</string>
<string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"pristup Bluetooth podešavanjima"</string>
<string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Dozvoljava aplikaciji da konfiguriše lokalni Bluetooth tablet, kao i da otkrije daljinske uređaje i upari se sa njima."</string>
<string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Dozvoljava aplikaciji da konfiguriše Bluetooth na Android TV uređaju i da otkrije udaljene uređaje i upari se sa njima."</string>
@@ -1149,6 +1149,10 @@
<string name="capital_off" msgid="7443704171014626777">"NE"</string>
<string name="checked" msgid="9179896827054513119">"označeno je"</string>
<string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Dovrši radnju preko"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Završi radnju"</string>
@@ -1263,7 +1267,7 @@
<string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"Zvuci alarma"</string>
<string name="ringtone_picker_title_notification" msgid="6387191794719608122">"Zvuci obaveštenja"</string>
<string name="ringtone_unknown" msgid="5059495249862816475">"Nepoznato"</string>
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Prijavljivanje na Wi-Fi mrežu"</string>
+ <string name="wifi_available_sign_in" msgid="381054692557675237">"Prijavljivanje na WiFi mrežu"</string>
<string name="network_available_sign_in" msgid="1520342291829283114">"Prijavite se na mrežu"</string>
<!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
<skip />
@@ -1279,7 +1283,7 @@
<string name="network_switch_metered_toast" msgid="501662047275723743">"Prešli ste sa tipa mreže <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na tip mreže <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
<item msgid="2255670471736226365">"mobilni podaci"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
+ <item msgid="5520925862115353992">"WiFi"</item>
<item msgid="1055487873974272842">"Bluetooth"</item>
<item msgid="1616528372438698248">"Eternet"</item>
<item msgid="9177085807664964627">"VPN"</item>
@@ -1542,10 +1546,10 @@
<string name="data_usage_warning_title" msgid="9034893717078325845">"Upozorenje na potrošnju podataka"</string>
<string name="data_usage_warning_body" msgid="1669325367188029454">"Potrošili ste <xliff:g id="APP">%s</xliff:g> podataka"</string>
<string name="data_usage_mobile_limit_title" msgid="3911447354393775241">"Dostigli ste ograničenje podataka"</string>
- <string name="data_usage_wifi_limit_title" msgid="2069698056520812232">"Nema više Wi-Fi podataka"</string>
+ <string name="data_usage_wifi_limit_title" msgid="2069698056520812232">"Nema više WiFi podataka"</string>
<string name="data_usage_limit_body" msgid="3567699582000085710">"Podaci su pauzirani tokom ostatka ciklusa"</string>
<string name="data_usage_mobile_limit_snoozed_title" msgid="101888478915677895">"Potrošili ste mobilne podatke"</string>
- <string name="data_usage_wifi_limit_snoozed_title" msgid="1622359254521960508">"Potrošili ste Wi-Fi podatke"</string>
+ <string name="data_usage_wifi_limit_snoozed_title" msgid="1622359254521960508">"Potrošili ste WiFi podatke"</string>
<string name="data_usage_limit_snoozed_body" msgid="545146591766765678">"Prekoračili ste <xliff:g id="SIZE">%s</xliff:g> od podešenog ograničenja"</string>
<string name="data_usage_restricted_title" msgid="126711424380051268">"Pozadinski podaci su ograničeni"</string>
<string name="data_usage_restricted_body" msgid="5338694433686077733">"Dodirnite za uklanjanje ograničenja."</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 6a0ad98..38da725 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1169,6 +1169,10 @@
<string name="capital_off" msgid="7443704171014626777">"Выключана"</string>
<string name="checked" msgid="9179896827054513119">"пазначана"</string>
<string name="not_checked" msgid="7972320087569023342">"не пазначана"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Завяршыць дзеянне з дапамогай"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завяршыць дзеянне з дапамогай %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Завяршыць дзеянне"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index cdd0ec0..3fca6eb 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"ИЗКЛ"</string>
<string name="checked" msgid="9179896827054513119">"с отметка"</string>
<string name="not_checked" msgid="7972320087569023342">"без отметка"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Изпълняване на действието чрез"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завършване на действието посредством %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Изпълняване на действието"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 779fd3e..bebece8 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"বন্ধ আছে"</string>
<string name="checked" msgid="9179896827054513119">"টিকচিহ্ন দেওয়া আছে"</string>
<string name="not_checked" msgid="7972320087569023342">"টিকচিহ্ন দেওয়া নেই"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"এটি ব্যবহার করে ক্রিয়াকলাপ সম্পূর্ণ করুন"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ব্যবহার করে ক্রিয়াকলাপ সম্পূর্ণ করুন"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ক্রিয়াকলাপ সম্পূর্ণ করুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index fab6c91..eb626f2 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1149,6 +1149,10 @@
<string name="capital_off" msgid="7443704171014626777">"Isključeno"</string>
<string name="checked" msgid="9179896827054513119">"označeno"</string>
<string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Završite radnju pomoću aplikacije"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Izvršiti akciju"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 96c6b6a..dd6e5a3 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"NO"</string>
<string name="checked" msgid="9179896827054513119">"seleccionat"</string>
<string name="not_checked" msgid="7972320087569023342">"no seleccionat"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Completa l\'acció mitjançant"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completa l\'acció amb %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completa l\'acció"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 9a5f069..d9f09a2 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1169,6 +1169,10 @@
<string name="capital_off" msgid="7443704171014626777">"O"</string>
<string name="checked" msgid="9179896827054513119">"vybráno"</string>
<string name="not_checked" msgid="7972320087569023342">"nevybráno"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Dokončit akci pomocí aplikace"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončit akci pomocí aplikace %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Dokončit akci"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 480e87c..d37cee4 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"FRA"</string>
<string name="checked" msgid="9179896827054513119">"slået til"</string>
<string name="not_checked" msgid="7972320087569023342">"slået fra"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Brug"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Gennemfør handling ved hjælp af %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Afslut handling"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ac9a597..4ab45b43 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"AUS"</string>
<string name="checked" msgid="9179896827054513119">"aktiviert"</string>
<string name="not_checked" msgid="7972320087569023342">"deaktiviert"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Aktion durchführen mit"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Aktion mit %1$s abschließen"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Abschließen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 6f03f80..933ddf7 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"Ανενεργό"</string>
<string name="checked" msgid="9179896827054513119">"επιλεγμένο"</string>
<string name="not_checked" msgid="7972320087569023342">"μη επιλεγμένο"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Ολοκλήρωση ενέργειας με τη χρήση"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Ολοκληρωμένη ενέργεια με χρήση %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Ολοκλήρωση ενέργειας"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index fac2f22..ef5a92a 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1129,6 +1129,8 @@
<string name="capital_off" msgid="7443704171014626777">"OFF"</string>
<string name="checked" msgid="9179896827054513119">"ticked"</string>
<string name="not_checked" msgid="7972320087569023342">"not ticked"</string>
+ <string name="selected" msgid="6614607926197755875">"selected"</string>
+ <string name="not_selected" msgid="410652016565864475">"not selected"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 8497658..6dae39b 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1129,6 +1129,8 @@
<string name="capital_off" msgid="7443704171014626777">"OFF"</string>
<string name="checked" msgid="9179896827054513119">"checked"</string>
<string name="not_checked" msgid="7972320087569023342">"not checked"</string>
+ <string name="selected" msgid="6614607926197755875">"selected"</string>
+ <string name="not_selected" msgid="410652016565864475">"not selected"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 12881e7..62e42b3 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1129,6 +1129,8 @@
<string name="capital_off" msgid="7443704171014626777">"OFF"</string>
<string name="checked" msgid="9179896827054513119">"ticked"</string>
<string name="not_checked" msgid="7972320087569023342">"not ticked"</string>
+ <string name="selected" msgid="6614607926197755875">"selected"</string>
+ <string name="not_selected" msgid="410652016565864475">"not selected"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index cb2eb7a..8579628 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1129,6 +1129,8 @@
<string name="capital_off" msgid="7443704171014626777">"OFF"</string>
<string name="checked" msgid="9179896827054513119">"ticked"</string>
<string name="not_checked" msgid="7972320087569023342">"not ticked"</string>
+ <string name="selected" msgid="6614607926197755875">"selected"</string>
+ <string name="not_selected" msgid="410652016565864475">"not selected"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 5750e50..6674ecd 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1129,6 +1129,8 @@
<string name="capital_off" msgid="7443704171014626777">"OFF"</string>
<string name="checked" msgid="9179896827054513119">"checked"</string>
<string name="not_checked" msgid="7972320087569023342">"not checked"</string>
+ <string name="selected" msgid="6614607926197755875">"selected"</string>
+ <string name="not_selected" msgid="410652016565864475">"not selected"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 8e7d1a33..d88f580 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"No"</string>
<string name="checked" msgid="9179896827054513119">"activado"</string>
<string name="not_checked" msgid="7972320087569023342">"desactivado"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Completar la acción mediante"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completar acción con %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completar acción"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index b9f0f58..a498c6a 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"DESACTIVADO"</string>
<string name="checked" msgid="9179896827054513119">"seleccionado"</string>
<string name="not_checked" msgid="7972320087569023342">"no seleccionado"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Completar acción utilizando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completar acción con %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completar acción"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index d825429..05cd7eb 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"VÄLJAS"</string>
<string name="checked" msgid="9179896827054513119">"märgitud"</string>
<string name="not_checked" msgid="7972320087569023342">"märkimata"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Lõpetage toiming rakendusega"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Toimingu lõpetamine, kasutades rakendust %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Vii toiming lõpule"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 05d4050..5f63703 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"DESAKTIBATUTA"</string>
<string name="checked" msgid="9179896827054513119">"markatuta"</string>
<string name="not_checked" msgid="7972320087569023342">"markatu gabe"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Gauzatu ekintza hau erabilita:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Osatu ekintza %1$s erabiliz"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Osatu ekintza"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 2146554..00956a4 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"خاموش"</string>
<string name="checked" msgid="9179896827054513119">"علامتزدهشده"</string>
<string name="not_checked" msgid="7972320087569023342">"بدون علامت"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"تکمیل کنش بااستفاده از"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"تکمیل کنش بااستفاده از %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"تکمیل عملکرد"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index e7bcf48..4e48801 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"POIS"</string>
<string name="checked" msgid="9179896827054513119">"valittu"</string>
<string name="not_checked" msgid="7972320087569023342">"ei valittu"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Tee toiminto käyttäen sovellusta"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Suorita sovelluksella %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Suorita toiminto"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index aad5749..283588e 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"NON"</string>
<string name="checked" msgid="9179896827054513119">"coché"</string>
<string name="not_checked" msgid="7972320087569023342">"non coché"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Continuer avec"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Continuer avec %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Terminer l\'action"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index d2bdbbb..83ba1e9 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"NON"</string>
<string name="checked" msgid="9179896827054513119">"activé"</string>
<string name="not_checked" msgid="7972320087569023342">"désactivé"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Continuer avec"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Terminer l\'action avec %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Terminer l\'action"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 79380d5..dbe9593 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"NON"</string>
<string name="checked" msgid="9179896827054513119">"seleccionado"</string>
<string name="not_checked" msgid="7972320087569023342">"non seleccionado"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Completar a acción usando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completar a acción usando %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completar acción"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index bf4d6c0..d417c90 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"બંધ"</string>
<string name="checked" msgid="9179896827054513119">"ચેક કર્યું"</string>
<string name="not_checked" msgid="7972320087569023342">"ચેક કર્યું નથી"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"આના ઉપયોગથી ક્રિયા પૂર્ણ કરો"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ઉપયોગથી ક્રિયા પૂર્ણ કરો"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ક્રિયા પૂર્ણ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index b867e9d..eac5f2d 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"बंद"</string>
<string name="checked" msgid="9179896827054513119">"चालू है"</string>
<string name="not_checked" msgid="7972320087569023342">"बंद है"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"इसका इस्तेमाल करके कार्रवाई को पूरा करें"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s का उपयोग करके कार्रवाई पूरी करें"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"कार्रवाई पूरी करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index dd2bdbe..1c80380 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1149,6 +1149,10 @@
<string name="capital_off" msgid="7443704171014626777">"Isklj."</string>
<string name="checked" msgid="9179896827054513119">"potvrđeno"</string>
<string name="not_checked" msgid="7972320087569023342">"nije potvrđeno"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Radnju dovrši pomoću stavke"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dovršavanje radnje pomoću aplikacije %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Dovrši radnju"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 9d1c2f4..68ba61e 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"Ki"</string>
<string name="checked" msgid="9179896827054513119">"kiválasztva"</string>
<string name="not_checked" msgid="7972320087569023342">"nincs kiválasztva"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Művelet végrehajtása a következővel:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Művelet elvégzése a(z) %1$s segítségével"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Művelet végrehajtása"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 6fade98..6c52232 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"O"</string>
<string name="checked" msgid="9179896827054513119">"նշված է"</string>
<string name="not_checked" msgid="7972320087569023342">"նշված չէ"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Ավարտել գործողությունը` օգտագործելով"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Եզրափակել գործողությունը՝ օգտագործելով %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Ավարտել գործողությունը"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 202664f..b420b6e 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"MATI"</string>
<string name="checked" msgid="9179896827054513119">"dicentang"</string>
<string name="not_checked" msgid="7972320087569023342">"tidak dicentang"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Selesaikan tindakan menggunakan"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Selesaikan tindakan menggunakan %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Selesaikan tindakan"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index efb23d4..a4390e3 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"SLÖKKT"</string>
<string name="checked" msgid="9179896827054513119">"valið"</string>
<string name="not_checked" msgid="7972320087569023342">"ekki valið"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Ljúka aðgerð með"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Ljúka aðgerð með %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Ljúka aðgerð"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index ec446d7..9835e8b 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"OFF"</string>
<string name="checked" msgid="9179896827054513119">"selezionato"</string>
<string name="not_checked" msgid="7972320087569023342">"deselezionato"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Completa l\'azione con"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completamento azione con %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completa azione"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 874d577..db190f7 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1169,6 +1169,10 @@
<string name="capital_off" msgid="7443704171014626777">"כבוי"</string>
<string name="checked" msgid="9179896827054513119">"מסומן"</string>
<string name="not_checked" msgid="7972320087569023342">"לא מסומן"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"השלמת פעולה באמצעות"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"להשלמת הפעולה באמצעות %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"השלם פעולה"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 32a4ded..d9c74c5 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"OFF"</string>
<string name="checked" msgid="9179896827054513119">"ON"</string>
<string name="not_checked" msgid="7972320087569023342">"OFF"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"アプリケーションを選択"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$sを使用してアクションを完了"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"アクションを実行"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 349bd16..491793d 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"გამორთ."</string>
<string name="checked" msgid="9179896827054513119">"მონიშნულია"</string>
<string name="not_checked" msgid="7972320087569023342">"არ არის მონიშნული"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"რა გამოვიყენოთ?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"მოქმედების %1$s-ის გამოყენებით დასრულება"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"მოქმედების დასრულება"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 4d4eef0..857892b 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"Өшірулі"</string>
<string name="checked" msgid="9179896827054513119">"белгіленген"</string>
<string name="not_checked" msgid="7972320087569023342">"белгіленбеген"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Әрекетті аяқтау"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Әрекетті %1$s қолданбасын пайдаланып аяқтау"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Әрекетті аяқтау"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 3ebd736..45d7ff7 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"បិទ"</string>
<string name="checked" msgid="9179896827054513119">"បានធីក"</string>
<string name="not_checked" msgid="7972320087569023342">"មិនបានធីក"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"បញ្ចប់សកម្មភាពដោយប្រើ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"បញ្ចប់សកម្មភាពដោយប្រើ %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"បញ្ចប់សកម្មភាព"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 0cf6f49..dc8f237 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"ಆಫ್ ಮಾಡು"</string>
<string name="checked" msgid="9179896827054513119">"ಪರಿಶೀಲಿಸಲಾಗಿದೆ"</string>
<string name="not_checked" msgid="7972320087569023342">"ಪರಿಶೀಲಿಸಲಾಗಿಲ್ಲ"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"ಇದನ್ನು ಬಳಸಿಕೊಂಡು ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ಬಳಸಿಕೊಂಡು ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 3dfdf3d..0e746f9 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"OFF"</string>
<string name="checked" msgid="9179896827054513119">"선택함"</string>
<string name="not_checked" msgid="7972320087569023342">"선택 안함"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"작업을 수행할 때 사용하는 애플리케이션"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s을(를) 사용하여 작업 완료"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"작업 완료"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 012acf5..d12ec8b 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"ӨЧҮК"</string>
<string name="checked" msgid="9179896827054513119">"белгиленген"</string>
<string name="not_checked" msgid="7972320087569023342">"белгилене элек"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Кайсынысын колдоносуз?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s аркылуу аракетти аягына чейин чыгаруу"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Аракетти аягына чыгаруу"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 315ee32..5d15180 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"ປິດ"</string>
<string name="checked" msgid="9179896827054513119">"ໝາຍຖືກແລ້ວ"</string>
<string name="not_checked" msgid="7972320087569023342">"ບໍ່ໄດ້ໝາຍຖືກ"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"ດຳເນີນການໂດຍໃຊ້"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"ສຳເລັດການດຳເນີນການໂດຍໃຊ້ %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ສຳເລັດຄຳສັ່ງ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 1d6b2c7..bef1f26 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1169,6 +1169,10 @@
<string name="capital_off" msgid="7443704171014626777">"IŠJ."</string>
<string name="checked" msgid="9179896827054513119">"pažymėta"</string>
<string name="not_checked" msgid="7972320087569023342">"nepažymėta"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Užbaigti veiksmą naudojant"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Užbaigti veiksmą naudojant %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Užbaigti veiksmą"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index ba790a0..44cf3a2 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1149,6 +1149,10 @@
<string name="capital_off" msgid="7443704171014626777">"IZSL."</string>
<string name="checked" msgid="9179896827054513119">"atzīmēts"</string>
<string name="not_checked" msgid="7972320087569023342">"nav atzīmēts"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Izvēlieties lietotni"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Pabeigt darbību, izmantojot %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Pabeigt darbību"</string>
diff --git a/core/res/res/values-mcc310-mnc560-as/strings.xml b/core/res/res/values-mcc310-mnc560-as/strings.xml
new file mode 100644
index 0000000..13f3657
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc560-as/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, 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:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="3526528316378889524">"ছিমখন প্ৰ\'ভিজন কৰা হোৱা নাই MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="4618730283812066268">"ছিমৰ অনুমতি নাই MM#3"</string>
+ <string name="mmcc_illegal_me" msgid="8522039751358990401">"ফ\'নৰ অনুমতি নাই MM#6"</string>
+</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 6f82395..15c7eba 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"ИСКЛУЧЕНО"</string>
<string name="checked" msgid="9179896827054513119">"штиклирано"</string>
<string name="not_checked" msgid="7972320087569023342">"не е штиклирано"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Активирај со"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Остварете го дејството со %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Заврши го дејството"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index ffe073c..314442b 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"ഓഫ്"</string>
<string name="checked" msgid="9179896827054513119">"പരിശോധിച്ചത്"</string>
<string name="not_checked" msgid="7972320087569023342">"പരിശോധിക്കാത്തത്"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"പൂർണ്ണമായ പ്രവർത്തനം ഉപയോഗിക്കുന്നു"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ഉപയോഗിച്ച് പ്രവർത്തനം പൂർത്തിയാക്കുക"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"പ്രവർത്തനം പൂർത്തിയാക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 2183d010..f4c78e8 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"Идэвхгүй"</string>
<string name="checked" msgid="9179896827054513119">"тэмдэглэсэн"</string>
<string name="not_checked" msgid="7972320087569023342">"тэмдэглээгүй"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Үйлдлийг дуусгах"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ашиглан үйлдлийг гүйцээх"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Үйлдлийг дуусгах"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 9b4849f..db5603c 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -167,15 +167,15 @@
<string name="httpErrorFailedSslHandshake" msgid="546319061228876290">"सुरक्षित कनेक्शन इंस्टॉल करू शकलो नाही."</string>
<string name="httpErrorBadUrl" msgid="754447723314832538">"URL अवैध असल्यामुळे पेज उघडू शकलो नाही."</string>
<string name="httpErrorFile" msgid="3400658466057744084">"फायलीवर प्रवेश करू शकलो नाही."</string>
- <string name="httpErrorFileNotFound" msgid="5191433324871147386">"विनंती केलेली फाईल शोधू शकलो नाही."</string>
+ <string name="httpErrorFileNotFound" msgid="5191433324871147386">"विनंती केलेली फाइल शोधू शकलो नाही."</string>
<string name="httpErrorTooManyRequests" msgid="2149677715552037198">"बर्याच विनंत्यांवर प्रक्रिया होत आहे. नंतर पुन्हा प्रयत्न करा."</string>
<string name="notification_title" msgid="5783748077084481121">"<xliff:g id="ACCOUNT">%1$s</xliff:g> साठी साइन इन एरर"</string>
<string name="contentServiceSync" msgid="2341041749565687871">"सिंक करा"</string>
<string name="contentServiceSyncNotificationTitle" msgid="5766411446676388623">"सिंक करू शकत नाही"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="4562226280528716090">"खूप जास्त <xliff:g id="CONTENT_TYPE">%s</xliff:g> हटवण्याचा प्रयत्न केला."</string>
- <string name="low_memory" product="tablet" msgid="5557552311566179924">"टॅबलेट संचयन पूर्ण भरले आहे. स्थान मोकळे करण्यासाठी काही फाईल हटवा."</string>
+ <string name="low_memory" product="tablet" msgid="5557552311566179924">"टॅबलेट संचयन पूर्ण भरले आहे. स्थान मोकळे करण्यासाठी काही फाइल हटवा."</string>
<string name="low_memory" product="watch" msgid="3479447988234030194">"पाहण्याचे संचयन पूर्ण भरले आहे. स्थान मोकळे करण्यासाठी काही फाइल हटवा."</string>
- <string name="low_memory" product="tv" msgid="6663680413790323318">"Android TV डिव्हाइस स्टोरेज पूर्ण भरलेले आहे. जागा मोकळी करण्यासाठी काही फाईल हटवा."</string>
+ <string name="low_memory" product="tv" msgid="6663680413790323318">"Android TV डिव्हाइस स्टोरेज पूर्ण भरलेले आहे. जागा मोकळी करण्यासाठी काही फाइल हटवा."</string>
<string name="low_memory" product="default" msgid="2539532364144025569">"फोन संचयन पूर्ण भरले आहे. स्थान मोकळे करण्यासाठी काही फाइल हटवा."</string>
<plurals name="ssl_ca_cert_warning" formatted="false" msgid="2288194355006173029">
<item quantity="other">प्रमाणपत्र अधिकार इंस्टॉल केले</item>
@@ -415,11 +415,11 @@
<string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"हृदय गती सारख्या, आपल्या शारीरिक स्थितीचे नियंत्रण करणार्या सेन्सरवरून डेटामध्ये प्रवेश करण्यासाठी ॲपला अनुमती देते."</string>
<string name="permlab_readCalendar" msgid="6408654259475396200">"कॅलेंडर इव्हेंट आणि तपशील वाचा"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"हा अॅप आपल्या टॅब्लेटवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
- <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"हे ॲप तुमच्या Android TV डिव्हाइसवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
+ <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"हे अॅप तुमच्या Android TV डिव्हाइसवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
<string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"हा अॅप आपल्या फोनवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string>
<string name="permlab_writeCalendar" msgid="6422137308329578076">"कॅलेंडर इव्हेंट जोडा किंवा बदला आणि मालकाला न कळवता अतिथींना ईमेल पाठवा"</string>
<string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"हा अॅप आपल्या टॅब्लेटवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
- <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"हे ॲप तुमच्या Android TV डिव्हाइसवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हे अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
+ <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"हे अॅप तुमच्या Android TV डिव्हाइसवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हे अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
<string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"हा अॅप आपल्या फोनवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
<string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"अतिरिक्त स्थान प्रदाता आदेश अॅक्सेस करा"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"अॅपला अतिरिक्त स्थान प्रदाता आदेशावर प्रवेश करण्याची अनुमती देते. हे कदाचित अॅपला GPS किंवा इतर स्थान स्रोत च्या ऑपरेशनमध्ये हस्तक्षेप करण्याची अनुमती देऊ शकते."</string>
@@ -881,11 +881,11 @@
<string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"खाते अनलॉक करा"</string>
<string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"बरेच पॅटर्न प्रयत्न"</string>
<string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"अनलॉक करण्यासाठी, आपल्या Google खात्यासह साइन इन करा."</string>
- <string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"वापरकर्तानाव (ईमेल)"</string>
+ <string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"वापरकर्ता नाव (ईमेल)"</string>
<string name="lockscreen_glogin_password_hint" msgid="3031027901286812848">"पासवर्ड"</string>
<string name="lockscreen_glogin_submit_button" msgid="3590556636347843733">"साइन इन करा"</string>
- <string name="lockscreen_glogin_invalid_input" msgid="4369219936865697679">"अवैध वापरकर्तानाव किंवा पासवर्ड."</string>
- <string name="lockscreen_glogin_account_recovery_hint" msgid="1683405808525090649">"तुमचे वापरकर्तानाव किंवा पासवर्ड विसरलात?\n "<b>"google.com/accounts/recovery"</b>" ला भेट द्या."</string>
+ <string name="lockscreen_glogin_invalid_input" msgid="4369219936865697679">"अवैध वापरकर्ता नाव किंवा पासवर्ड."</string>
+ <string name="lockscreen_glogin_account_recovery_hint" msgid="1683405808525090649">"तुमचे वापरकर्ता नाव किंवा पासवर्ड विसरलात?\n "<b>"google.com/accounts/recovery"</b>" ला भेट द्या."</string>
<string name="lockscreen_glogin_checking_password" msgid="2607271802803381645">"तपासत आहे..."</string>
<string name="lockscreen_unlock_label" msgid="4648257878373307582">"अनलॉक करा"</string>
<string name="lockscreen_sound_on_label" msgid="1660281470535492430">"ध्वनी सुरू"</string>
@@ -961,7 +961,7 @@
<string name="permdesc_readHistoryBookmarks" msgid="2323799501008967852">"ब्राउझरने भेट दिलेल्या सर्व URL चा इतिहास आणि ब्राउझरचे सर्व बुकमार्क वाचण्यास अॅप ला अनुमती देते. टीप: या परवानगीची तृतीय-पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमता असलेल्या अन्य अनुप्रयोगांद्वारे अंमलबजावणी करू शकत नाही."</string>
<string name="permlab_writeHistoryBookmarks" msgid="6090259925187986937">"वेब बुकमार्क आणि इतिहास लिहा"</string>
<string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="573341025292489065">"तुमच्या टॅब्लेटवर स्टोअर केलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी अॅप ला अनुमती देते. हे ब्राउझर डेटा मिटविण्यासाठी किंवा सुधारित करण्यासाठी अॅप ला अनुमती देते. टीप: ही परवानगी तृतीय पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमतांसह अन्य अॅप्लिकेशनद्वारे अंमलबजावणी करण्याची टीप देऊ शकते."</string>
- <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"तुमच्या Android TV डिव्हाइसवर साठवलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी ॲप ला अनुमती देते. हे ॲपला ब्राउझर डेटा मिटवण्याची किंवा सुधारित करण्याची परवानगी देते. टीप: ही परवानगी तृतीय पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमतांसह अन्य अॅप्लिकेशनद्वारे अंमलबजावणी करण्याची टीप देऊ शकते."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"तुमच्या Android TV डिव्हाइसवर साठवलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी अॅप ला अनुमती देते. हे अॅपला ब्राउझर डेटा मिटवण्याची किंवा सुधारित करण्याची परवानगी देते. टीप: ही परवानगी तृतीय पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमतांसह अन्य अॅप्लिकेशनद्वारे अंमलबजावणी करण्याची टीप देऊ शकते."</string>
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="2245203087160913652">"तुमच्या फोनवर स्टोअर केलेला ब्राउझरचा इतिहास किंवा बुकमार्क सुधारित करण्यासाठी अॅप ला अनुमती देते. हे ब्राउझर डेटा मिटविण्यासाठी किंवा सुधारित करण्यासाठी अॅप ला अनुमती देते. टीप: ही परवानगी तृतीय पक्ष ब्राउझरद्वारे किंवा वेब ब्राउझिंग क्षमतांसह अन्य अॅप्लिकेशनद्वारे अंमलबजावणी करण्याची टीप देऊ शकते."</string>
<string name="permlab_setAlarm" msgid="1158001610254173567">"अलार्म सेट करा"</string>
<string name="permdesc_setAlarm" msgid="2185033720060109640">"इंस्टॉल केलेल्या अलार्म घड्याळ ॲपमध्ये अलार्म सेट करण्यासाठी ॲपला अनुमती देते. काही अलार्म घड्याळ ॲप्समध्ये हे वैशिष्ट्य नसू शकते."</string>
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"बंद"</string>
<string name="checked" msgid="9179896827054513119">"तपासले"</string>
<string name="not_checked" msgid="7972320087569023342">"तपासले नाही"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"याचा वापर करून क्रिया पूर्ण करा"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s वापरून क्रिया पूर्ण करा"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"क्रिया पूर्ण झाली"</string>
@@ -1440,8 +1444,8 @@
<string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"कायम सुरू असलेल्या VPN मधून डिस्कनेक्ट केले"</string>
<string name="vpn_lockdown_error" msgid="4453048646854247947">"कायम सुरू असलेल्या VPN शी कनेक्ट करता आले नाही"</string>
<string name="vpn_lockdown_config" msgid="8331697329868252169">"नेटवर्क किंवा VPN सेटिंग्ज बदला"</string>
- <string name="upload_file" msgid="8651942222301634271">"फाईल निवडा"</string>
- <string name="no_file_chosen" msgid="4146295695162318057">"फाईल निवडली नाही"</string>
+ <string name="upload_file" msgid="8651942222301634271">"फाइल निवडा"</string>
+ <string name="no_file_chosen" msgid="4146295695162318057">"फाइल निवडली नाही"</string>
<string name="reset" msgid="3865826612628171429">"रीसेट करा"</string>
<string name="submit" msgid="862795280643405865">"सबमिट करा"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ड्रायव्हिंग अॅप सुरू आहे"</string>
@@ -1603,11 +1607,11 @@
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="4705368340409816254">"पिन कोड जुळत नाहीत"</string>
<string name="kg_login_too_many_attempts" msgid="699292728290654121">"बरेच पॅटर्न प्रयत्न"</string>
<string name="kg_login_instructions" msgid="3619844310339066827">"अनलॉक करण्यासाठी, आपल्या Google खात्यासह साइन इन करा."</string>
- <string name="kg_login_username_hint" msgid="1765453775467133251">"वापरकर्तानाव (ईमेल)"</string>
+ <string name="kg_login_username_hint" msgid="1765453775467133251">"वापरकर्ता नाव (ईमेल)"</string>
<string name="kg_login_password_hint" msgid="3330530727273164402">"पासवर्ड"</string>
<string name="kg_login_submit_button" msgid="893611277617096870">"साइन इन करा"</string>
- <string name="kg_login_invalid_input" msgid="8292367491901220210">"अवैध वापरकर्तानाव किंवा पासवर्ड."</string>
- <string name="kg_login_account_recovery_hint" msgid="4892466171043541248">"तुमचे वापरकर्तानाव किंवा पासवर्ड विसरलात?\n "<b>"google.com/accounts/recovery"</b>" ला भेट द्या."</string>
+ <string name="kg_login_invalid_input" msgid="8292367491901220210">"अवैध वापरकर्ता नाव किंवा पासवर्ड."</string>
+ <string name="kg_login_account_recovery_hint" msgid="4892466171043541248">"तुमचे वापरकर्ता नाव किंवा पासवर्ड विसरलात?\n "<b>"google.com/accounts/recovery"</b>" ला भेट द्या."</string>
<string name="kg_login_checking_password" msgid="4676010303243317253">"खाते तपासत आहे…"</string>
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"तुम्ही तुमचा पिन <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने टाइप केला आहे. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"तुम्ही तुमचा पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने टाइप केला आहे. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
@@ -1961,7 +1965,7 @@
<string name="autofill_save_type_debit_card" msgid="3169397504133097468">"डेबिट कार्ड"</string>
<string name="autofill_save_type_payment_card" msgid="6555012156728690856">"पेमेंट कार्ड"</string>
<string name="autofill_save_type_generic_card" msgid="1019367283921448608">"कार्ड"</string>
- <string name="autofill_save_type_username" msgid="1018816929884640882">"वापरकर्तानाव"</string>
+ <string name="autofill_save_type_username" msgid="1018816929884640882">"वापरकर्ता नाव"</string>
<string name="autofill_save_type_email_address" msgid="1303262336895591924">"ईमेल ॲड्रेस"</string>
<string name="etws_primary_default_message_earthquake" msgid="8401079517718280669">"शांत रहा आणि जवळपास निवारा शोधा."</string>
<string name="etws_primary_default_message_tsunami" msgid="5828171463387976279">"किनारपट्टीचे प्रदेश आणि नदीकाठची क्षेत्रे त्वरित रिकामी करून उंच मैदानासारख्या अधिक सुरक्षित ठिकाणी जा."</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 5c35ba1..6d33cfa 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"MATIKAN"</string>
<string name="checked" msgid="9179896827054513119">"ditandai"</string>
<string name="not_checked" msgid="7972320087569023342">"tidak ditandai"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Selesaikan tindakan menggunakan"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Selesaikan tindakan menggunakan %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Selesaikan tindakan"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 38f1aa8..df33b37 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"ပိတ်"</string>
<string name="checked" msgid="9179896827054513119">"အမှန်ခြစ်ပြီး"</string>
<string name="not_checked" msgid="7972320087569023342">"ခြစ် မထား"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"အောက်ပါတို့ကို အသုံးပြုမှု အပြီးသတ်ခြင်း"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ကို သုံးပြီး လုပ်ဆောင်ချက် ပြီးဆုံးပါစေ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"လုပ်ဆောင်ချက်ကို အပြီးသတ်ပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 5b0a4ad..e62bf51 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"Av"</string>
<string name="checked" msgid="9179896827054513119">"avmerket"</string>
<string name="not_checked" msgid="7972320087569023342">"ikke avmerket"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Fullfør med"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Fullfør handlingen med %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Fullfør handlingen"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 0326c23..b5fd7b2 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"बन्द"</string>
<string name="checked" msgid="9179896827054513119">"जाँच गरिएको"</string>
<string name="not_checked" msgid="7972320087569023342">"जाँच गरिएको छैन"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"प्रयोग गरेर कारबाही पुरा गर्नुहोस्"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"निम्न एपको प्रयोग गरी कारबाही पुरा गर्नुहोस्: %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"पूर्ण कारबाही"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 17e988e4..c041cb5 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"UIT"</string>
<string name="checked" msgid="9179896827054513119">"aangevinkt"</string>
<string name="not_checked" msgid="7972320087569023342">"niet aangevinkt"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Actie voltooien met"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Actie voltooien via %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Actie voltooien"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 0f10cde..81a9676 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"ବନ୍ଦ"</string>
<string name="checked" msgid="9179896827054513119">"ଯାଞ୍ଚ ହୋଇଛି"</string>
<string name="not_checked" msgid="7972320087569023342">"ଯାଞ୍ଚ ହୋଇନାହିଁ"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"ବ୍ୟବହାର କରି କାର୍ଯ୍ୟ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ବ୍ୟବହାର କରି କାର୍ଯ୍ୟ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"କାର୍ଯ୍ୟ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 11bfd78..2df3c44 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"ਬੰਦ"</string>
<string name="checked" msgid="9179896827054513119">"ਨਿਸ਼ਾਨਬੱਧ ਕੀਤਾ ਗਿਆ"</string>
<string name="not_checked" msgid="7972320087569023342">"ਨਿਸ਼ਾਨਬੱਧ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"ਇਸਨੂੰ ਵਰਤਦੇ ਹੋਏ ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ਵਰਤਦੇ ਹੋਏ ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index d626513..3962f74 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1169,6 +1169,10 @@
<string name="capital_off" msgid="7443704171014626777">"Wył."</string>
<string name="checked" msgid="9179896827054513119">"wybrano"</string>
<string name="not_checked" msgid="7972320087569023342">"nie wybrano"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Wykonaj czynność przez..."</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Wykonaj czynność w aplikacji %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Wykonaj działanie"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 450fddb..72a867e 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"DESL"</string>
<string name="checked" msgid="9179896827054513119">"marcado"</string>
<string name="not_checked" msgid="7972320087569023342">"não marcado"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Complete a ação usando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir a ação usando %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Concluir ação"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 6862001..28b83c1 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1129,13 +1129,17 @@
<string name="capital_off" msgid="7443704171014626777">"Desativado"</string>
<string name="checked" msgid="9179896827054513119">"selecionado"</string>
<string name="not_checked" msgid="7972320087569023342">"não selecionado"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Concluir ação utilizando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir ação utilizando %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Concluir ação"</string>
<string name="whichViewApplication" msgid="5733194231473132945">"Abrir com"</string>
<string name="whichViewApplicationNamed" msgid="415164730629690105">"Abrir com %1$s"</string>
<string name="whichViewApplicationLabel" msgid="7367556735684742409">"Abrir"</string>
- <string name="whichOpenHostLinksWith" msgid="7645631470199397485">"Abra os links de <xliff:g id="HOST">%1$s</xliff:g> com:"</string>
+ <string name="whichOpenHostLinksWith" msgid="7645631470199397485">"Abrir os links de <xliff:g id="HOST">%1$s</xliff:g> com:"</string>
<string name="whichOpenLinksWith" msgid="1120936181362907258">"Abrir os links com:"</string>
<string name="whichOpenLinksWithApp" msgid="6917864367861910086">"Abra os links com a app <xliff:g id="APPLICATION">%1$s</xliff:g>"</string>
<string name="whichOpenHostLinksWithApp" msgid="2401668560768463004">"Abra os links de <xliff:g id="HOST">%1$s</xliff:g> com a app <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 450fddb..72a867e 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"DESL"</string>
<string name="checked" msgid="9179896827054513119">"marcado"</string>
<string name="not_checked" msgid="7972320087569023342">"não marcado"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Complete a ação usando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir a ação usando %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Concluir ação"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index f9e608f..bbe35ed 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1149,6 +1149,10 @@
<string name="capital_off" msgid="7443704171014626777">"NU"</string>
<string name="checked" msgid="9179896827054513119">"bifat"</string>
<string name="not_checked" msgid="7972320087569023342">"nebifat"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Finalizare acțiune utilizând"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Finalizați acțiunea utilizând %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Finalizați acțiunea"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index be24770..2903ba2 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -300,7 +300,7 @@
<string name="safeMode" msgid="8974401416068943888">"Безопасный режим"</string>
<string name="android_system_label" msgid="5974767339591067210">"Система Android"</string>
<string name="user_owner_label" msgid="8628726904184471211">"Переключиться на личный профиль"</string>
- <string name="managed_profile_label" msgid="7316778766973512382">"Переключиться на рабочий профиль"</string>
+ <string name="managed_profile_label" msgid="7316778766973512382">"Перейти в рабочий профиль"</string>
<string name="permgrouplab_contacts" msgid="4254143639307316920">"Контакты"</string>
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"доступ к контактам"</string>
<string name="permgrouplab_location" msgid="1858277002233964394">"Местоположение"</string>
@@ -1169,6 +1169,10 @@
<string name="capital_off" msgid="7443704171014626777">"O"</string>
<string name="checked" msgid="9179896827054513119">"отмечено"</string>
<string name="not_checked" msgid="7972320087569023342">"не отмечено"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Что использовать?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Выполнить с помощью приложения \"%1$s\""</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Выполнить действие"</string>
@@ -1692,7 +1696,7 @@
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Выберите функции, которые будут запускаться с помощью кнопки специальных возможностей"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Выберите функции, которые будут запускаться с помощью кнопки регулировки громкости"</string>
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Сервис \"<xliff:g id="SERVICE_NAME">%s</xliff:g>\" отключен."</string>
- <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Изменить быстрые клавиши"</string>
+ <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Изменить ярлыки"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Готово"</string>
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Деактивировать быстрое включение"</string>
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Использовать быстрое включение"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index c23e475..70cfaa7 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1129,6 +1129,8 @@
<string name="capital_off" msgid="7443704171014626777">"ක්රියාවිරහිතයි"</string>
<string name="checked" msgid="9179896827054513119">"පරීක්ෂා කර ඇත"</string>
<string name="not_checked" msgid="7972320087569023342">"පරීක්ෂා කර නැත"</string>
+ <string name="selected" msgid="6614607926197755875">"තෝරන ලදි"</string>
+ <string name="not_selected" msgid="410652016565864475">"තෝරා නොමැත"</string>
<string name="whichApplication" msgid="5432266899591255759">"පහත භාවිතයෙන් ක්රියාව සම්පූර්ණ කරන්න"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s භාවිතා කරමින් ක්රියාව සම්පුර්ණ කරන්න"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ක්රියාව සම්පූර්ණ කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 980a945..f6c1b89 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1169,6 +1169,10 @@
<string name="capital_off" msgid="7443704171014626777">"O"</string>
<string name="checked" msgid="9179896827054513119">"začiarknuté"</string>
<string name="not_checked" msgid="7972320087569023342">"nezačiarknuté"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Dokončiť akciu pomocou aplikácie"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončiť akciu pomocou aplikácie %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Dokončiť akciu"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 573a082..2f2fcc2 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1169,6 +1169,10 @@
<string name="capital_off" msgid="7443704171014626777">"IZKLOPLJENO"</string>
<string name="checked" msgid="9179896827054513119">"potrjeno"</string>
<string name="not_checked" msgid="7972320087569023342">"ni potrjeno"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Dokončanje dejanja z aplikacijo"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončanje dejanja z aplikacijo %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Izvedba dejanja"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index f6f3594..a36e860 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"Çaktivizuar"</string>
<string name="checked" msgid="9179896827054513119">"u përzgjodh"</string>
<string name="not_checked" msgid="7972320087569023342">"nuk u përzgjodh"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Përfundo veprimin duke përdorur"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Përfundo veprimin duke përdorur %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Përfundo veprimin"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 366d29b..f9968ef 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -94,7 +94,7 @@
<string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"Статус мобилних података"</string>
<string name="notification_channel_sms" msgid="1243384981025535724">"SMS-ови"</string>
<string name="notification_channel_voice_mail" msgid="8457433203106654172">"Поруке говорне поште"</string>
- <string name="notification_channel_wfc" msgid="9048240466765169038">"Позивање преко Wi-Fi мреже"</string>
+ <string name="notification_channel_wfc" msgid="9048240466765169038">"Позивање преко WiFi мреже"</string>
<string name="notification_channel_sim" msgid="5098802350325677490">"Статус SIM-а"</string>
<string name="notification_channel_sim_high_prio" msgid="642361929452850928">"Обавештења SIM картице са статусом „висок приоритет“"</string>
<string name="peerTtyModeFull" msgid="337553730440832160">"Корисник захтева ПОТПУН режим TTY"</string>
@@ -123,30 +123,30 @@
<string name="roamingText11" msgid="5245687407203281407">"Банер роминга је укључен"</string>
<string name="roamingText12" msgid="673537506362152640">"Банер роминга је искључен"</string>
<string name="roamingTextSearching" msgid="5323235489657753486">"Претраживање услуге"</string>
- <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Подешавање позивања преко Wi-Fi-ја није успело"</string>
+ <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Подешавање позивања преко WiFi-ја није успело"</string>
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="468830943567116703">"Да бисте упућивали позиве и слали поруке преко Wi-Fi-ја, прво затражите од мобилног оператера да вам омогући ову услугу. Затим у Подешавањима поново укључите Позивање преко Wi-Fi-ја. (кôд грешке: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
+ <item msgid="468830943567116703">"Да бисте упућивали позиве и слали поруке преко WiFi-ја, прво затражите од мобилног оператера да вам омогући ову услугу. Затим у Подешавањима поново укључите Позивање преко WiFi-ја. (кôд грешке: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="4795145070505729156">"Проблем у вези са регистровањем позивања преко Wi‑Fi-ја код мобилног оператера: <xliff:g id="CODE">%1$s</xliff:g>"</item>
</string-array>
<!-- no translation found for wfcSpnFormat_spn (2982505428519096311) -->
<skip />
- <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"<xliff:g id="SPN">%s</xliff:g> позивање преко Wi-Fi-ја"</string>
- <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"<xliff:g id="SPN">%s</xliff:g> – позивање преко Wi-Fi-ја"</string>
+ <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"<xliff:g id="SPN">%s</xliff:g> позивање преко WiFi-ја"</string>
+ <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"<xliff:g id="SPN">%s</xliff:g> – позивање преко WiFi-ја"</string>
<string name="wfcSpnFormat_wlan_call" msgid="4895315549916165700">"WLAN позив"</string>
<string name="wfcSpnFormat_spn_wlan_call" msgid="255919245825481510">"<xliff:g id="SPN">%s</xliff:g> WLAN позив"</string>
- <string name="wfcSpnFormat_spn_wifi" msgid="7232899594327126970">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string>
- <string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="8383917598312067365">"Позивање преко Wi-Fi-ја | <xliff:g id="SPN">%s</xliff:g>"</string>
+ <string name="wfcSpnFormat_spn_wifi" msgid="7232899594327126970">"<xliff:g id="SPN">%s</xliff:g> WiFi"</string>
+ <string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="8383917598312067365">"Позивање преко WiFi-ја | <xliff:g id="SPN">%s</xliff:g>"</string>
<string name="wfcSpnFormat_spn_vowifi" msgid="6865214948822061486">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string>
- <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Позивање преко Wi-Fi-ја"</string>
- <string name="wfcSpnFormat_wifi" msgid="1376356951297043426">"Wi-Fi"</string>
- <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Позивање преко Wi-Fi-ја"</string>
+ <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Позивање преко WiFi-ја"</string>
+ <string name="wfcSpnFormat_wifi" msgid="1376356951297043426">"WiFi"</string>
+ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Позивање преко WiFi-ја"</string>
<string name="wfcSpnFormat_vowifi" msgid="8371335230890725606">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="5626710010766902560">"Искључено"</string>
- <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Позивање преко Wi-Fi-ја"</string>
+ <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Позивање преко WiFi-ја"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Позив преко мобилне мреже"</string>
- <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Само Wi-Fi"</string>
+ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Само WiFi"</string>
<string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Није прослеђено"</string>
<string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> након <xliff:g id="TIME_DELAY">{2}</xliff:g> секунде/и"</string>
@@ -501,14 +501,14 @@
<string name="permdesc_changeNetworkState" msgid="649341947816898736">"Дозвољава апликацији да мења статус повезивања са мрежом."</string>
<string name="permlab_changeTetherState" msgid="9079611809931863861">"промена повезивања привезивањем"</string>
<string name="permdesc_changeTetherState" msgid="3025129606422533085">"Дозвољава апликацији да мења статус везе са привезаном мрежом."</string>
- <string name="permlab_accessWifiState" msgid="5552488500317911052">"преглед Wi-Fi веза"</string>
- <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Дозвољава апликацији да прегледа информације о Wi-Fi умрежавању, као што су информације о томе да ли је Wi-Fi омогућен и називи повезаних Wi-Fi уређаја."</string>
- <string name="permlab_changeWifiState" msgid="7947824109713181554">"повезивање и прекид везе са Wi-Fi мрежом"</string>
- <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Дозвољава апликацији да се повезује са приступним тачкама за Wi-Fi и прекида везу са њима, као и да уноси промене у конфигурацију уређаја за Wi-Fi мреже."</string>
- <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"омогућавање пријема вишесмерног Wi-Fi саобраћаја"</string>
- <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Дозвољава апликацији да прима пакете који се шаљу на све уређаје на Wi-Fi мрежи помоћу вишесмерних адреса, а не само на таблет. Користи више напајања од режима једносмерног саобраћаја."</string>
- <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Дозвољава апликацији да прима пакете који се шаљу на све уређаје на Wi-Fi мрежи помоћу вишесмерних адреса, а не само на Android TV уређај. Користи више енергије од режима без вишесмерног слања."</string>
- <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Дозвољава апликацији да прима пакете који се шаљу на све уређаје на Wi-Fi мрежи помоћу вишесмерних адреса, а не само на телефон. Користи више напајања од режима једносмерног саобраћаја."</string>
+ <string name="permlab_accessWifiState" msgid="5552488500317911052">"преглед WiFi веза"</string>
+ <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Дозвољава апликацији да прегледа информације о WiFi умрежавању, као што су информације о томе да ли је WiFi омогућен и називи повезаних WiFi уређаја."</string>
+ <string name="permlab_changeWifiState" msgid="7947824109713181554">"повезивање и прекид везе са WiFi мрежом"</string>
+ <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Дозвољава апликацији да се повезује са приступним тачкама за WiFi и прекида везу са њима, као и да уноси промене у конфигурацију уређаја за WiFi мреже."</string>
+ <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"омогућавање пријема вишесмерног WiFi саобраћаја"</string>
+ <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Дозвољава апликацији да прима пакете који се шаљу на све уређаје на WiFi мрежи помоћу вишесмерних адреса, а не само на таблет. Користи више напајања од режима једносмерног саобраћаја."</string>
+ <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Дозвољава апликацији да прима пакете који се шаљу на све уређаје на WiFi мрежи помоћу вишесмерних адреса, а не само на Android TV уређај. Користи више енергије од режима без вишесмерног слања."</string>
+ <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Дозвољава апликацији да прима пакете који се шаљу на све уређаје на WiFi мрежи помоћу вишесмерних адреса, а не само на телефон. Користи више напајања од режима једносмерног саобраћаја."</string>
<string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"приступ Bluetooth подешавањима"</string>
<string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Дозвољава апликацији да конфигурише локални Bluetooth таблет, као и да открије даљинске уређаје и упари се са њима."</string>
<string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Дозвољава апликацији да конфигурише Bluetooth на Android TV уређају и да открије удаљене уређаје и упари се са њима."</string>
@@ -1149,6 +1149,10 @@
<string name="capital_off" msgid="7443704171014626777">"НЕ"</string>
<string name="checked" msgid="9179896827054513119">"означено је"</string>
<string name="not_checked" msgid="7972320087569023342">"није означено"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Доврши радњу преко"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завршите радњу помоћу апликације %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Заврши радњу"</string>
@@ -1263,7 +1267,7 @@
<string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"Звуци аларма"</string>
<string name="ringtone_picker_title_notification" msgid="6387191794719608122">"Звуци обавештења"</string>
<string name="ringtone_unknown" msgid="5059495249862816475">"Непознато"</string>
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Пријављивање на Wi-Fi мрежу"</string>
+ <string name="wifi_available_sign_in" msgid="381054692557675237">"Пријављивање на WiFi мрежу"</string>
<string name="network_available_sign_in" msgid="1520342291829283114">"Пријавите се на мрежу"</string>
<!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
<skip />
@@ -1279,7 +1283,7 @@
<string name="network_switch_metered_toast" msgid="501662047275723743">"Прешли сте са типа мреже <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на тип мреже <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
<item msgid="2255670471736226365">"мобилни подаци"</item>
- <item msgid="5520925862115353992">"Wi-Fi"</item>
+ <item msgid="5520925862115353992">"WiFi"</item>
<item msgid="1055487873974272842">"Bluetooth"</item>
<item msgid="1616528372438698248">"Етернет"</item>
<item msgid="9177085807664964627">"VPN"</item>
@@ -1542,10 +1546,10 @@
<string name="data_usage_warning_title" msgid="9034893717078325845">"Упозорење на потрошњу података"</string>
<string name="data_usage_warning_body" msgid="1669325367188029454">"Потрошили сте <xliff:g id="APP">%s</xliff:g> података"</string>
<string name="data_usage_mobile_limit_title" msgid="3911447354393775241">"Достигли сте ограничење података"</string>
- <string name="data_usage_wifi_limit_title" msgid="2069698056520812232">"Нема више Wi-Fi података"</string>
+ <string name="data_usage_wifi_limit_title" msgid="2069698056520812232">"Нема више WiFi података"</string>
<string name="data_usage_limit_body" msgid="3567699582000085710">"Подаци су паузирани током остатка циклуса"</string>
<string name="data_usage_mobile_limit_snoozed_title" msgid="101888478915677895">"Потрошили сте мобилне податке"</string>
- <string name="data_usage_wifi_limit_snoozed_title" msgid="1622359254521960508">"Потрошили сте Wi-Fi податке"</string>
+ <string name="data_usage_wifi_limit_snoozed_title" msgid="1622359254521960508">"Потрошили сте WiFi податке"</string>
<string name="data_usage_limit_snoozed_body" msgid="545146591766765678">"Прекорачили сте <xliff:g id="SIZE">%s</xliff:g> од подешеног ограничења"</string>
<string name="data_usage_restricted_title" msgid="126711424380051268">"Позадински подаци су ограничени"</string>
<string name="data_usage_restricted_body" msgid="5338694433686077733">"Додирните за уклањање ограничења."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 988fb93..de346c6 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"AV"</string>
<string name="checked" msgid="9179896827054513119">"markerad"</string>
<string name="not_checked" msgid="7972320087569023342">"inte markerad"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Slutför åtgärd genom att använda"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Slutför åtgärden med %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Slutför åtgärd"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index fd390d8..0d56560 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"ZIMA"</string>
<string name="checked" msgid="9179896827054513119">"imeteuliwa"</string>
<string name="not_checked" msgid="7972320087569023342">"haijateuliwa"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Kamilisha kitendo ukitumia"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Kamilisha kitendo ukitumia %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Kamilisha kitendo"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index c764a20..f694144 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"ஆஃப்"</string>
<string name="checked" msgid="9179896827054513119">"இயக்கப்பட்டுள்ளது"</string>
<string name="not_checked" msgid="7972320087569023342">"முடக்கப்பட்டுள்ளது"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"இதைப் பயன்படுத்தி செயலை நிறைவுசெய்"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ஐப் பயன்படுத்தி செயலை முடிக்கவும்"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"செயலை முடி"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 23292a2..1247dc1 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"ఆఫ్"</string>
<string name="checked" msgid="9179896827054513119">"ఎంచుకోబడింది"</string>
<string name="not_checked" msgid="7972320087569023342">"ఎంచుకోలేదు"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"దీన్ని ఉపయోగించి చర్యను పూర్తి చేయండి"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$sను ఉపయోగించి చర్యను పూర్తి చేయి"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"చర్యను పూర్తి చేయి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index d5d21c6..4cc2e16 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"ปิด"</string>
<string name="checked" msgid="9179896827054513119">"เลือกไว้"</string>
<string name="not_checked" msgid="7972320087569023342">"ยังไม่เลือก"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"ทำงานให้เสร็จโดยใช้"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"ดำเนินการให้เสร็จสมบูรณ์โดยใช้ %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ทำงานให้เสร็จสิ้น"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index f53851d..02c6f87 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"I-OFF"</string>
<string name="checked" msgid="9179896827054513119">"nilagyan ng check"</string>
<string name="not_checked" msgid="7972320087569023342">"hindi nilagyan ng check"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Kumpletuhin ang pagkilos gamit ang"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Tapusin ang pagkilos gamit ang %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Gawin ang pagkilos"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 5aa336e..08f07f20 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"KAPALI"</string>
<string name="checked" msgid="9179896827054513119">"işaretli"</string>
<string name="not_checked" msgid="7972320087569023342">"işaretli değil"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"İşlemi şunu kullanarak tamamla"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"İşlemi %1$s kullanarak tamamla"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"İşlemi tamamla"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index da41508..a802933 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1169,6 +1169,10 @@
<string name="capital_off" msgid="7443704171014626777">"ВИМК"</string>
<string name="checked" msgid="9179896827054513119">"вибрано"</string>
<string name="not_checked" msgid="7972320087569023342">"не вибрано"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Що використовувати?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завершити дію за допомогою %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Завершити дію"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 698e9c4..e2901bc 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"آف"</string>
<string name="checked" msgid="9179896827054513119">"چیک کیا گیا"</string>
<string name="not_checked" msgid="7972320087569023342">"چیک نہیں کیا گیا"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"اس کا استعمال کرکے کارروائی مکمل کریں"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s کا استعمال کر کے کارروائی مکمل کریں"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"کارروائی مکمل کریں"</string>
@@ -2077,7 +2081,7 @@
<string name="PERSOSUBSTATE_SIM_NETWORK_ENTRY" msgid="8050953231914637819">"SIM نیٹ ورک غیر مقفل کرنے کا PIN"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ENTRY" msgid="7164399703751688214">"SIM نیٹ ورک سب سیٹ کو غیر مقفل کرنے کا PIN"</string>
<string name="PERSOSUBSTATE_SIM_CORPORATE_ENTRY" msgid="4447629474818217364">"SIM کارپوریٹ کو غیر مقفل کرنے کا PIN"</string>
- <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ENTRY" msgid="973059024670737358">"SIM کے خدمت کے فراہم کنندہ کو غیر مقفل کرنے کا PIN"</string>
+ <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ENTRY" msgid="973059024670737358">"SIM کے سروس فراہم کنندہ کو غیر مقفل کرنے کا PIN"</string>
<string name="PERSOSUBSTATE_SIM_SIM_ENTRY" msgid="4487435301206073787">"SIM کو غیر مقفل کرنے کا PIN"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_PUK_ENTRY" msgid="768060297218652809">"PUK درج کریں"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_ENTRY" msgid="7129527319490548930">"PUK درج کریں"</string>
@@ -2088,7 +2092,7 @@
<string name="PERSOSUBSTATE_RUIM_NETWORK2_ENTRY" msgid="687618528751880721">"RUIM network2 کو غیر مقفل کرنے کا PIN"</string>
<string name="PERSOSUBSTATE_RUIM_HRPD_ENTRY" msgid="6810596579655575381">"RUIM hrpd کو غیر مقفل کرنے کا PIN"</string>
<string name="PERSOSUBSTATE_RUIM_CORPORATE_ENTRY" msgid="2715929642540980259">"RUIM کارپوریٹ کو غیر مقفل کرنے کا PIN"</string>
- <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ENTRY" msgid="8557791623303951590">"RUIM خدمت کے فراہم کنندہ کو غیر مقفل کرنے کا PIN"</string>
+ <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ENTRY" msgid="8557791623303951590">"RUIM سروس فراہم کنندہ کو غیر مقفل کرنے کا PIN"</string>
<string name="PERSOSUBSTATE_RUIM_RUIM_ENTRY" msgid="7382468767274580323">"RUIM کو غیر مقفل کرنے کا PIN"</string>
<string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_ENTRY" msgid="6730880791104286987">"PUK درج کریں"</string>
<string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_ENTRY" msgid="6432126539782267026">"PUK درج کریں"</string>
@@ -2100,10 +2104,10 @@
<string name="PERSOSUBSTATE_SIM_SP_EHPLMN_ENTRY" msgid="3988705848553894358">"SP Equivalent Home PLMN کو غیر مقفل کرنے کا PIN"</string>
<string name="PERSOSUBSTATE_SIM_ICCID_ENTRY" msgid="6186770686690993200">"ICCID کو غیر مقفل کرنے کا PIN"</string>
<string name="PERSOSUBSTATE_SIM_IMPI_ENTRY" msgid="7043865376145617024">"IMPI کو غیر مقفل کرنے کا PIN"</string>
- <string name="PERSOSUBSTATE_SIM_NS_SP_ENTRY" msgid="6144227308185112176">"نیٹ ورک سب سیٹ خدمت کے فراہم کنندہ کو غیر مقفل کرنے کا PIN"</string>
+ <string name="PERSOSUBSTATE_SIM_NS_SP_ENTRY" msgid="6144227308185112176">"نیٹ ورک سب سیٹ سروس فراہم کنندہ کو غیر مقفل کرنے کا PIN"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_IN_PROGRESS" msgid="4233355366318061180">"SIM نیٹ ورک کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_IN_PROGRESS" msgid="6742563947637715645">"SIM نیٹ ورک سب سیٹ کو غیر مقفل کرنے کی درخواست کی جا رہی ہے …"</string>
- <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_IN_PROGRESS" msgid="2033399698172403560">"SIM کے خدمت کے فراہم کنندہ کو غیر مقفل کرنے کی درخواست کی جار ہی ہے…"</string>
+ <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_IN_PROGRESS" msgid="2033399698172403560">"SIM کے سروس فراہم کنندہ کو غیر مقفل کرنے کی درخواست کی جار ہی ہے…"</string>
<string name="PERSOSUBSTATE_SIM_CORPORATE_IN_PROGRESS" msgid="4795977251920732254">"SIM کارپوریٹ کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_PUK_IN_PROGRESS" msgid="1090425878157254446">"PUK کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_IN_PROGRESS" msgid="6476898876518094438">"PUK کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
@@ -2114,13 +2118,13 @@
<string name="PERSOSUBSTATE_RUIM_NETWORK1_IN_PROGRESS" msgid="4013870911606478520">"RUIM network1 کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
<string name="PERSOSUBSTATE_RUIM_NETWORK2_IN_PROGRESS" msgid="9032651188219523434">"RUIM network2 کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
<string name="PERSOSUBSTATE_RUIM_HRPD_IN_PROGRESS" msgid="6584576506344491207">"RUIM hrpd کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
- <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_IN_PROGRESS" msgid="830981927724888114">"RUIM خدمت کے فراہم کنندہ کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
+ <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_IN_PROGRESS" msgid="830981927724888114">"RUIM سروس فراہم کنندہ کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
<string name="PERSOSUBSTATE_RUIM_CORPORATE_IN_PROGRESS" msgid="7851790973098894802">"RUIM کارپوریٹ کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
<string name="PERSOSUBSTATE_SIM_SPN_IN_PROGRESS" msgid="1149560739586960121">"SPN کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
<string name="PERSOSUBSTATE_SIM_SP_EHPLMN_IN_PROGRESS" msgid="5708964693522116025">"Requesting SP Equivalent Home کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
<string name="PERSOSUBSTATE_SIM_ICCID_IN_PROGRESS" msgid="7288103122966483455">"ICCID کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
<string name="PERSOSUBSTATE_SIM_IMPI_IN_PROGRESS" msgid="4036752174056147753">"IMPI کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
- <string name="PERSOSUBSTATE_SIM_NS_SP_IN_PROGRESS" msgid="5089536274515338566">"نیٹ ورک سب سیٹ کے خدمت کے فراہم کنندہ کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
+ <string name="PERSOSUBSTATE_SIM_NS_SP_IN_PROGRESS" msgid="5089536274515338566">"نیٹ ورک سب سیٹ کے سروس فراہم کنندہ کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
<string name="PERSOSUBSTATE_RUIM_RUIM_IN_PROGRESS" msgid="6737197986936251958">"RUIM کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
<string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_IN_PROGRESS" msgid="5658767775619998623">"PUK کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
<string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_IN_PROGRESS" msgid="665978313257653727">"PUK کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
@@ -2130,14 +2134,14 @@
<string name="PERSOSUBSTATE_RUIM_RUIM_PUK_IN_PROGRESS" msgid="1230605365926493599">"PUK کو غیر مقفل کرنے کی درخواست کی جا رہی ہے…"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_ERROR" msgid="1924844017037151535">"SIM نیٹ ورک کو غیر مقفل کرنے کی درخواست ناکام ہو گئی۔"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ERROR" msgid="3372797822292089708">"SIM نیٹ ورک سب سیٹ کو غیر مقفل کرنے کی درخواست ناکام ہو گئی۔"</string>
- <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ERROR" msgid="1878443146720411381">"SIM کے خدمت کے فراہم کنندہ کو غیر مقفل کرنے کی درخواست ناکام ہو گئی۔"</string>
+ <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ERROR" msgid="1878443146720411381">"SIM کے سروس فراہم کنندہ کو غیر مقفل کرنے کی درخواست ناکام ہو گئی۔"</string>
<string name="PERSOSUBSTATE_SIM_CORPORATE_ERROR" msgid="7664778312218023192">"SIM کارپوریٹ کو غیر مقفل کرنے کی درخواست ناکام ہو گئی۔"</string>
<string name="PERSOSUBSTATE_SIM_SIM_ERROR" msgid="2472944311643350302">"SIM کو غیر مقفل کرنے کی درخواست ناکام ہو گئی۔"</string>
<string name="PERSOSUBSTATE_RUIM_NETWORK1_ERROR" msgid="828089694480999120">"RUIM Network1 کو غیر مقفل کرنے کی درخواست ناکام ہو گئی۔"</string>
<string name="PERSOSUBSTATE_RUIM_NETWORK2_ERROR" msgid="17619001007092511">"RUIM Network2 غیر مقفل کرنے کی درخواست ناکام ہو گئی۔"</string>
<string name="PERSOSUBSTATE_RUIM_HRPD_ERROR" msgid="807214229604353614">"RUIM Hrpd کو غیر مقفل کرنے کی درخواست ناکام ہو گئی۔"</string>
<string name="PERSOSUBSTATE_RUIM_CORPORATE_ERROR" msgid="8644184447744175747">"RUIM کارپوریٹ کو غیر مقفل کرنے کی درخواست ناکام ہو گئی۔"</string>
- <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ERROR" msgid="3801002648649640407">"RUIM خدمت کے فراہم کنندہ کو غیر مقفل کرنے کی درخواست ناکام ہو گئی۔"</string>
+ <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ERROR" msgid="3801002648649640407">"RUIM سروس فراہم کنندہ کو غیر مقفل کرنے کی درخواست ناکام ہو گئی۔"</string>
<string name="PERSOSUBSTATE_RUIM_RUIM_ERROR" msgid="707397021218680753">"RUIM کو غیر مقفل کرنے کی درخواست ناکام ہو گئی۔"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_PUK_ERROR" msgid="894358680773257820">"PUK غیر مقفل نہیں ہو سکا۔"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_ERROR" msgid="352466878146726991">"PUK غیر مقفل نہیں ہو سکا۔"</string>
@@ -2154,16 +2158,16 @@
<string name="PERSOSUBSTATE_SIM_SP_EHPLMN_ERROR" msgid="1116993930995545742">"SP Equivalent Home PLMN کو غیر مقفل کرنے کی درخواست ناکام ہو گئی۔"</string>
<string name="PERSOSUBSTATE_SIM_ICCID_ERROR" msgid="7559167306794441462">"ICCID کو غیر مقفل کرنے کی درخواست ناکام ہو گئی۔"</string>
<string name="PERSOSUBSTATE_SIM_IMPI_ERROR" msgid="2782926139511136588">"IMPI کو غیر مقفل کرنے کی درخواست ناکام ہو گئی۔"</string>
- <string name="PERSOSUBSTATE_SIM_NS_SP_ERROR" msgid="1890493954453456758">"نیٹ ورک سب سیٹ خدمت کے فراہم کنندہ کو غیر مقفل کرنے کی درخواست ناکام ہو گئی۔"</string>
+ <string name="PERSOSUBSTATE_SIM_NS_SP_ERROR" msgid="1890493954453456758">"نیٹ ورک سب سیٹ سروس فراہم کنندہ کو غیر مقفل کرنے کی درخواست ناکام ہو گئی۔"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_SUCCESS" msgid="4886243367747126325">"SIM نیٹ ورک غیر مقفل ہو گیا۔"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_SUCCESS" msgid="4053809277733513987">"SIM نیٹ ورک سب سیٹ غیر مقفل ہو گیا۔"</string>
- <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_SUCCESS" msgid="8249342930499801740">"SIM کے خدمت کا فراہم کنندہ غیر مقفل ہو گیا۔"</string>
+ <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_SUCCESS" msgid="8249342930499801740">"SIM کے سروس فراہم کنندہ غیر مقفل ہو گیا۔"</string>
<string name="PERSOSUBSTATE_SIM_CORPORATE_SUCCESS" msgid="2339794542560381270">"SIM کارپوریٹ غیر مقفل ہو گیا۔"</string>
<string name="PERSOSUBSTATE_SIM_SIM_SUCCESS" msgid="6975608174152828954">"SIM غیر مقفل ہو گیا۔"</string>
<string name="PERSOSUBSTATE_RUIM_NETWORK1_SUCCESS" msgid="2846699261330463192">"RUIM Network1 غیر مقفل ہو گیا۔"</string>
<string name="PERSOSUBSTATE_RUIM_NETWORK2_SUCCESS" msgid="5335414726057102801">"RUIM Network2 غیر مقفل ہو گیا۔"</string>
<string name="PERSOSUBSTATE_RUIM_HRPD_SUCCESS" msgid="8868100318474971969">"RUIM Hrpd غیر مقفل ہو گیا۔"</string>
- <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_SUCCESS" msgid="6020936629725666932">"RUIM خدمت کا فراہم کنندہ غیر مقفل ہو گیا۔"</string>
+ <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_SUCCESS" msgid="6020936629725666932">"RUIM سروس فراہم کنندہ غیر مقفل ہو گیا۔"</string>
<string name="PERSOSUBSTATE_RUIM_CORPORATE_SUCCESS" msgid="6944873647584595489">"RUIM کارپوریٹ غیر مقفل ہو گیا۔"</string>
<string name="PERSOSUBSTATE_RUIM_RUIM_SUCCESS" msgid="2526483514124121988">"RUIM غیر مقفل ہو گیا۔"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_PUK_SUCCESS" msgid="7662200333621664621">"PUK غیر مقفل ہو گیا۔"</string>
@@ -2181,7 +2185,7 @@
<string name="PERSOSUBSTATE_SIM_SP_EHPLMN_SUCCESS" msgid="8146602361895007345">"SP Equivalent Home PLMN غیر مقفل ہو گیا۔"</string>
<string name="PERSOSUBSTATE_SIM_ICCID_SUCCESS" msgid="8058678548991999545">"ICCID غیر مقفل ہو گیا۔"</string>
<string name="PERSOSUBSTATE_SIM_IMPI_SUCCESS" msgid="2545608067978550571">"IMPI غیر مقفل ہو گیا۔"</string>
- <string name="PERSOSUBSTATE_SIM_NS_SP_SUCCESS" msgid="4352382949744625007">"نیٹ ورک سب سیٹ کے خدمت کا فراہم کنندہ غیر مقفل ہو گیا۔"</string>
+ <string name="PERSOSUBSTATE_SIM_NS_SP_SUCCESS" msgid="4352382949744625007">"نیٹ ورک سب سیٹ کے سروس فراہم کنندہ غیر مقفل ہو گیا۔"</string>
<string name="config_pdp_reject_dialog_title" msgid="4072057179246785727"></string>
<string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
<string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 61d29dc..f49db45 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"O"</string>
<string name="checked" msgid="9179896827054513119">"belgilandi"</string>
<string name="not_checked" msgid="7972320087569023342">"belgilanmadi"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Nima ishlatilsin?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"“%1$s” bilan ochish"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Amalni bajarish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index fd7205d..17ef576 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"TẮT"</string>
<string name="checked" msgid="9179896827054513119">"đã chọn"</string>
<string name="not_checked" msgid="7972320087569023342">"chưa chọn"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Hoàn tất thao tác bằng"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Hoàn tất thao tác bằng %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Hoàn thành tác vụ"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 16775d5..0fe62a9 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"关闭"</string>
<string name="checked" msgid="9179896827054513119">"已勾选"</string>
<string name="not_checked" msgid="7972320087569023342">"未勾选"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"选择要使用的应用:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"使用%1$s完成操作"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"完成操作"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index bd13574..8f9c7c4 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"關"</string>
<string name="checked" msgid="9179896827054513119">"已勾選"</string>
<string name="not_checked" msgid="7972320087569023342">"未勾選"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"完成操作需使用"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"完成操作需使用 %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"完成操作"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 0637742..eba7978 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"關閉"</string>
<string name="checked" msgid="9179896827054513119">"已勾選"</string>
<string name="not_checked" msgid="7972320087569023342">"未勾選"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"選擇要使用的應用程式"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"完成操作需使用 %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"完成操作"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index ea97a6e..0f6fcb5 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1129,6 +1129,10 @@
<string name="capital_off" msgid="7443704171014626777">"VALIWE"</string>
<string name="checked" msgid="9179896827054513119">"kuhloliwe"</string>
<string name="not_checked" msgid="7972320087569023342">"akuhloliwe"</string>
+ <!-- no translation found for selected (6614607926197755875) -->
+ <skip />
+ <!-- no translation found for not_selected (410652016565864475) -->
+ <skip />
<string name="whichApplication" msgid="5432266899591255759">"Qedela isenzo usebenzisa"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Qedela isenzo usebenzisa i-%1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Qedela isenzo"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index e3ddbd8..f8266ba 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -3202,10 +3202,23 @@
<attr name="minHeight" />
<!-- Window layout affinity of this activity. Activities with the same window layout
- affinity will share the same layout record. If an activity is launched in freeform window,
- the activity will be launched to the latest position and size where any task, if the root
- activity of that task shares the same window layout affinity with the activity being
- launched. Window layout affinity is shared only among activities with the same UID.
+ affinity will share the same layout record. That is, if a user is opening an activity in
+ a new task on a display that can host freeform windows, and the user had opened a task
+ before and that task had a root activity who had the same window layout affinity, the
+ new task's window will be created in the same window mode and around the location which
+ the previously opened task was in.
+
+ <p>For example, if a user maximizes a task with root activity A and opens another
+ activity B that has the same window layout affinity as activity A has, activity B will
+ be created in fullscreen window mode. Similarly, if they move/resize a task with root
+ activity C and open another activity D that has the same window layout affinity as
+ activity C has, activity D will be in freeform window mode and as close to the position
+ of activity C as conditions permit. It doesn't require the user to keep the task with
+ activity A or activity C open. It won't, however, put any task into split-screen or PIP
+ window mode on launch.
+
+ <p>If the user is opening an activity with its window layout affinity for the first time,
+ the window mode and position is OEM defined.
<p>By default activity doesn't share any affinity with other activities. -->
<attr name="windowLayoutAffinity" format="string" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b4b634a..8d51294 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1857,6 +1857,8 @@
<string name="config_defaultCallScreening" translatable="false"></string>
<!-- The name of the package that will hold the system gallery role. -->
<string name="config_systemGallery" translatable="false">com.android.gallery3d</string>
+ <!-- The names of the packages that will hold the automotive projection role. -->
+ <string name="config_systemAutomotiveProjection" translatable="false"></string>
<!-- The name of the package that will hold the system cluster service role. -->
<string name="config_systemAutomotiveCluster" translatable="false"></string>
<!-- The name of the package that will hold the system video call role. -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index e6ebd4b..84e7d42 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -207,6 +207,9 @@
<!-- Default padding for dialogs. -->
<dimen name="dialog_padding">16dp</dimen>
+ <!-- The horizontal margin of the content in the notification shade -->
+ <dimen name="notification_shade_content_margin_horizontal">16dp</dimen>
+
<!-- The margin on the start of the content view -->
<dimen name="notification_content_margin_start">16dp</dimen>
@@ -857,4 +860,9 @@
<dimen name="waterfall_display_top_edge_size">0px</dimen>
<dimen name="waterfall_display_right_edge_size">0px</dimen>
<dimen name="waterfall_display_bottom_edge_size">0px</dimen>
+
+ <!-- The maximum height of a thumbnail in a ThumbnailTemplate. The image will be reduced to that height in case they are bigger. -->
+ <dimen name="controls_thumbnail_image_max_height">140dp</dimen>
+ <!-- The maximum width of a thumbnail in a ThumbnailTemplate. The image will be reduced to that width in case they are bigger.-->
+ <dimen name="controls_thumbnail_image_max_width">280dp</dimen>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 45bdff9..39989dd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3074,6 +3074,8 @@
<public name="config_systemAutomotiveCluster" />
<!-- @hide @SystemApi @TestApi -->
<public name="config_systemVideoCall" />
+ <!-- @hide @SystemApi @TestApi -->
+ <public name="config_systemAutomotiveProjection" />
</public-group>
<public-group type="id" first-id="0x01020055">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c2120de..8eb0853 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3056,10 +3056,14 @@
<!-- Default text for a button that can be toggled on and off. -->
<string name="capital_off">OFF</string>
- <!-- Default state for CompoundButton for on and off. [CHAR LIMIT=32] -->
+ <!-- Default checked text used by accessibility for a button that can be checked or unchecked. [CHAR LIMIT=NONE] -->
<string name="checked">checked</string>
- <!-- Default text for a button that can be toggled on and off. [CHAR LIMIT=32] -->
+ <!-- Default not checked text used by accessibility for a button that can be checked or unchecked. [CHAR LIMIT=NONE] -->
<string name="not_checked">not checked</string>
+ <!-- Default selected text used by accessibility for an element that can be selected or unselected. [CHAR LIMIT=NONE] -->
+ <string name="selected">selected</string>
+ <!-- Default not selected text used by accessibility for an element that can be selected or unselected. [CHAR LIMIT=NONE] -->
+ <string name="not_selected">not selected</string>
<!-- Title of intent resolver dialog when selecting an application to run. -->
<string name="whichApplication">Complete action using</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index caa3dff..697f7e0 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -826,6 +826,7 @@
<java-symbol type="string" name="no_file_chosen" />
<java-symbol type="string" name="no_matches" />
<java-symbol type="string" name="noon" />
+ <java-symbol type="string" name="not_selected" />
<java-symbol type="string" name="number_picker_increment_scroll_action" />
<java-symbol type="string" name="number_picker_increment_scroll_mode" />
<java-symbol type="string" name="old_app_action" />
@@ -959,6 +960,7 @@
<java-symbol type="string" name="save_password_never" />
<java-symbol type="string" name="save_password_notnow" />
<java-symbol type="string" name="save_password_remember" />
+ <java-symbol type="string" name="selected" />
<java-symbol type="string" name="sendText" />
<java-symbol type="string" name="sending" />
<java-symbol type="string" name="serviceClassData" />
@@ -2850,6 +2852,7 @@
<java-symbol type="id" name="header_text_secondary" />
<java-symbol type="id" name="expand_button" />
<java-symbol type="id" name="notification_header" />
+ <java-symbol type="id" name="notification_top_line" />
<java-symbol type="id" name="time_divider" />
<java-symbol type="id" name="header_text_divider" />
<java-symbol type="id" name="header_text_secondary_divider" />
@@ -2860,6 +2863,7 @@
<java-symbol type="drawable" name="ic_collapse_bundle" />
<java-symbol type="dimen" name="notification_min_content_height" />
<java-symbol type="dimen" name="notification_header_shrink_min_width" />
+ <java-symbol type="dimen" name="notification_shade_content_margin_horizontal" />
<java-symbol type="dimen" name="notification_content_margin_start" />
<java-symbol type="dimen" name="notification_content_margin_end" />
<java-symbol type="dimen" name="notification_reply_inset" />
@@ -4077,4 +4081,7 @@
<java-symbol type="array" name="config_keep_warming_services" />
<java-symbol type="string" name="config_display_features" />
<java-symbol type="array" name="config_internalFoldedPhysicalDisplayIds" />
+
+ <java-symbol type="dimen" name="controls_thumbnail_image_max_height" />
+ <java-symbol type="dimen" name="controls_thumbnail_image_max_width" />
</resources>
diff --git a/core/tests/coretests/apks/install_decl_perm/AndroidManifest.xml b/core/tests/coretests/apks/install_decl_perm/AndroidManifest.xml
index 9a1f0ff..6a0c421 100644
--- a/core/tests/coretests/apks/install_decl_perm/AndroidManifest.xml
+++ b/core/tests/coretests/apks/install_decl_perm/AndroidManifest.xml
@@ -17,17 +17,14 @@
package="com.android.frameworks.coretests.install_decl_perm">
<permission android:name="com.android.frameworks.coretests.NORMAL"
- android:permissionGroup="android.permission-group.COST_MONEY"
android:protectionLevel="normal"
android:label="test normal perm" />
<permission android:name="com.android.frameworks.coretests.DANGEROUS"
- android:permissionGroup="android.permission-group.COST_MONEY"
android:protectionLevel="dangerous"
android:label="test dangerous perm" />
<permission android:name="com.android.frameworks.coretests.SIGNATURE"
- android:permissionGroup="android.permission-group.COST_MONEY"
android:protectionLevel="signature"
android:label="test signature perm" />
diff --git a/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java b/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
new file mode 100644
index 0000000..faa67a8
--- /dev/null
+++ b/core/tests/coretests/src/android/os/CombinedVibrationEffectTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.os;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.testng.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@Presubmit
+@RunWith(JUnit4.class)
+public class CombinedVibrationEffectTest {
+
+ @Test
+ public void testValidateMono() {
+ CombinedVibrationEffect.createSynced(VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
+
+ assertThrows(IllegalArgumentException.class,
+ () -> CombinedVibrationEffect.createSynced(new VibrationEffect.OneShot(-1, -1)));
+ }
+
+ @Test
+ public void testSerializationMono() {
+ CombinedVibrationEffect original = CombinedVibrationEffect.createSynced(
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
+
+ Parcel parcel = Parcel.obtain();
+ original.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ CombinedVibrationEffect restored = CombinedVibrationEffect.CREATOR.createFromParcel(parcel);
+ assertEquals(original, restored);
+ }
+}
diff --git a/core/tests/coretests/src/android/os/VibrationEffectTest.java b/core/tests/coretests/src/android/os/VibrationEffectTest.java
index e685292..c357414 100644
--- a/core/tests/coretests/src/android/os/VibrationEffectTest.java
+++ b/core/tests/coretests/src/android/os/VibrationEffectTest.java
@@ -28,6 +28,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
import android.content.ContentInterface;
import android.content.ContentResolver;
@@ -100,6 +101,73 @@
}
@Test
+ public void testValidateOneShot() {
+ VibrationEffect.createOneShot(1, 255).validate();
+ VibrationEffect.createOneShot(1, VibrationEffect.DEFAULT_AMPLITUDE).validate();
+
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createOneShot(-1, 255).validate());
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createOneShot(0, 255).validate());
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createOneShot(1, -2).validate());
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createOneShot(1, 256).validate());
+ }
+
+ @Test
+ public void testValidatePrebaked() {
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK).validate();
+ VibrationEffect.createPredefined(VibrationEffect.RINGTONES[1]).validate();
+
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createPredefined(-1).validate());
+ }
+
+ @Test
+ public void testValidateWaveform() {
+ VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, -1).validate();
+ VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, 0).validate();
+
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createWaveform(new long[0], new int[0], -1).validate());
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createWaveform(TEST_TIMINGS, new int[0], -1).validate());
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createWaveform(
+ new long[]{0, 0, 0}, TEST_AMPLITUDES, -1).validate());
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createWaveform(
+ TEST_TIMINGS, new int[]{-1, -1, -2}, -1).validate());
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.createWaveform(
+ TEST_TIMINGS, TEST_AMPLITUDES, TEST_TIMINGS.length).validate());
+ }
+
+ @Test
+ public void testValidateComposed() {
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10)
+ .compose()
+ .validate();
+
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.startComposition().addPrimitive(-1).compose().validate());
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, -1, 10)
+ .compose()
+ .validate());
+ assertThrows(IllegalArgumentException.class,
+ () -> VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, -1)
+ .compose()
+ .validate());
+ }
+
+ @Test
public void testScalePrebaked_ignoresScaleAndReturnsSameEffect() {
VibrationEffect initial = VibrationEffect.get(VibrationEffect.RINGTONES[1]);
assertSame(initial, initial.scale(0.5f));
diff --git a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
index f4ebe2f..63e4642 100644
--- a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
+++ b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
@@ -32,6 +32,8 @@
import android.content.IIntentSender;
import android.content.Intent;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -39,11 +41,14 @@
import android.service.controls.actions.CommandAction;
import android.service.controls.actions.ControlAction;
import android.service.controls.actions.ControlActionWrapper;
+import android.service.controls.templates.ThumbnailTemplate;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.R;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -107,7 +112,8 @@
mPendingIntent = new PendingIntent(mIIntentSender);
- mControlsProviderService = new FakeControlsProviderService();
+ mControlsProviderService = new FakeControlsProviderService(
+ InstrumentationRegistry.getInstrumentation().getContext());
mControlsProvider = IControlsProvider.Stub.asInterface(
mControlsProviderService.onBind(intent));
}
@@ -134,7 +140,8 @@
verify(mSubscriber).onSubscribe(eq(mToken), subscriptionCaptor.capture());
subscriptionCaptor.getValue().request(1000);
- verify(mSubscriber, times(2)).onNext(eq(mToken), controlCaptor.capture());
+ verify(mSubscriber, times(2))
+ .onNext(eq(mToken), controlCaptor.capture());
List<Control> values = controlCaptor.getAllValues();
assertTrue(equals(values.get(0), list.get(0)));
assertTrue(equals(values.get(1), list.get(1)));
@@ -210,26 +217,69 @@
.setStatus(Control.STATUS_OK)
.build();
- @SuppressWarnings("unchecked")
- ArgumentCaptor<Control> controlCaptor =
- ArgumentCaptor.forClass(Control.class);
- ArgumentCaptor<IControlsSubscription.Stub> subscriptionCaptor =
- ArgumentCaptor.forClass(IControlsSubscription.Stub.class);
+ Control c = sendControlGetControl(control);
+ assertTrue(equals(c, control));
+ }
- ArrayList<Control> list = new ArrayList<>();
- list.add(control);
+ @Test
+ public void testThumbnailRescaled_bigger() throws RemoteException {
+ Context context = mControlsProviderService.getBaseContext();
+ int maxWidth = context.getResources().getDimensionPixelSize(
+ R.dimen.controls_thumbnail_image_max_width);
+ int maxHeight = context.getResources().getDimensionPixelSize(
+ R.dimen.controls_thumbnail_image_max_height);
- mControlsProviderService.setControls(list);
+ int min = Math.min(maxWidth, maxHeight);
+ int max = Math.max(maxWidth, maxHeight);
- mControlsProvider.subscribe(new ArrayList<String>(), mSubscriber);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ Bitmap bitmap = Bitmap.createBitmap(max * 2, max * 2, Bitmap.Config.ALPHA_8);
+ Icon icon = Icon.createWithBitmap(bitmap);
+ ThumbnailTemplate template = new ThumbnailTemplate("ID", false, icon, "");
- verify(mSubscriber).onSubscribe(eq(mToken), subscriptionCaptor.capture());
- subscriptionCaptor.getValue().request(1);
+ Control control = new Control.StatefulBuilder("TEST_ID", mPendingIntent)
+ .setTitle("TEST_TITLE")
+ .setStatus(Control.STATUS_OK)
+ .setControlTemplate(template)
+ .build();
- verify(mSubscriber).onNext(eq(mToken), controlCaptor.capture());
- Control c = controlCaptor.getValue();
- assertTrue(equals(c, list.get(0)));
+ Control c = sendControlGetControl(control);
+
+ ThumbnailTemplate sentTemplate = (ThumbnailTemplate) c.getControlTemplate();
+ Bitmap sentBitmap = sentTemplate.getThumbnail().getBitmap();
+
+ // Aspect ratio is kept
+ assertEquals(sentBitmap.getWidth(), sentBitmap.getHeight());
+
+ assertEquals(min, sentBitmap.getWidth());
+ }
+
+ @Test
+ public void testThumbnailRescaled_smaller() throws RemoteException {
+ Context context = mControlsProviderService.getBaseContext();
+ int maxWidth = context.getResources().getDimensionPixelSize(
+ R.dimen.controls_thumbnail_image_max_width);
+ int maxHeight = context.getResources().getDimensionPixelSize(
+ R.dimen.controls_thumbnail_image_max_height);
+
+ int min = Math.min(maxWidth, maxHeight);
+
+ Bitmap bitmap = Bitmap.createBitmap(min / 2, min / 2, Bitmap.Config.ALPHA_8);
+ Icon icon = Icon.createWithBitmap(bitmap);
+ ThumbnailTemplate template = new ThumbnailTemplate("ID", false, icon, "");
+
+ Control control = new Control.StatefulBuilder("TEST_ID", mPendingIntent)
+ .setTitle("TEST_TITLE")
+ .setStatus(Control.STATUS_OK)
+ .setControlTemplate(template)
+ .build();
+
+ Control c = sendControlGetControl(control);
+
+ ThumbnailTemplate sentTemplate = (ThumbnailTemplate) c.getControlTemplate();
+ Bitmap sentBitmap = sentTemplate.getThumbnail().getBitmap();
+
+ assertEquals(bitmap.getHeight(), sentBitmap.getHeight());
+ assertEquals(bitmap.getWidth(), sentBitmap.getWidth());
}
@Test
@@ -257,6 +307,32 @@
intent.getParcelableExtra(ControlsProviderService.EXTRA_CONTROL)));
}
+ /**
+ * Sends the control through the publisher in {@code mControlsProviderService}, returning
+ * the control obtained by the subscriber
+ */
+ private Control sendControlGetControl(Control control) throws RemoteException {
+ @SuppressWarnings("unchecked")
+ ArgumentCaptor<Control> controlCaptor =
+ ArgumentCaptor.forClass(Control.class);
+ ArgumentCaptor<IControlsSubscription.Stub> subscriptionCaptor =
+ ArgumentCaptor.forClass(IControlsSubscription.Stub.class);
+
+ ArrayList<Control> list = new ArrayList<>();
+ list.add(control);
+
+ mControlsProviderService.setControls(list);
+
+ mControlsProvider.subscribe(new ArrayList<String>(), mSubscriber);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ verify(mSubscriber).onSubscribe(eq(mToken), subscriptionCaptor.capture());
+ subscriptionCaptor.getValue().request(1);
+
+ verify(mSubscriber).onNext(eq(mToken), controlCaptor.capture());
+ return controlCaptor.getValue();
+ }
+
private static boolean equals(Control c1, Control c2) {
if (c1 == c2) return true;
if (c1 == null || c2 == null) return false;
@@ -276,6 +352,11 @@
static class FakeControlsProviderService extends ControlsProviderService {
+ FakeControlsProviderService(Context context) {
+ super();
+ attachBaseContext(context);
+ }
+
private List<Control> mControls;
public void setControls(List<Control> controls) {
diff --git a/core/tests/coretests/src/android/service/controls/templates/ControlTemplateTest.java b/core/tests/coretests/src/android/service/controls/templates/ControlTemplateTest.java
index 87dc1b7..91a3ba7 100644
--- a/core/tests/coretests/src/android/service/controls/templates/ControlTemplateTest.java
+++ b/core/tests/coretests/src/android/service/controls/templates/ControlTemplateTest.java
@@ -103,6 +103,17 @@
}
@Test
+ public void testUnparcelingCorrectClass_thumbnail() {
+ ControlTemplate toParcel = new ThumbnailTemplate(TEST_ID, false, mIcon,
+ TEST_ACTION_DESCRIPTION);
+
+ ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
+
+ assertEquals(ControlTemplate.TYPE_THUMBNAIL, fromParcel.getTemplateType());
+ assertTrue(fromParcel instanceof ThumbnailTemplate);
+ }
+
+ @Test
public void testUnparcelingCorrectClass_toggleRange() {
ControlTemplate toParcel = new ToggleRangeTemplate(TEST_ID, mControlButton,
new RangeTemplate(TEST_ID, 0, 2, 1, 1, "%f"));
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index 5362be3..a0fc349 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -853,6 +853,8 @@
@Test
public void testFormatSimple_Advanced() {
+ assertEquals("000000000000002a.ext",
+ formatSimple("%016x.%s", 42, "ext"));
assertEquals("crtcl=0x002a:intrsv=Y:grnk=0x0018:gsmry=A:example:rnk=0x0000",
formatSimple("crtcl=0x%04x:intrsv=%c:grnk=0x%04x:gsmry=%c:%s:rnk=0x%04x",
42, 'Y', 24, 'A', "example", 0));
diff --git a/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java b/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java
new file mode 100644
index 0000000..dfbc39c
--- /dev/null
+++ b/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.inputmethod;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SurroundingTextTest {
+
+ @Test
+ public void testSurroundingTextBasicCreation() {
+ SurroundingText surroundingText1 = new SurroundingText("test", 0, 0, 0);
+ assertThat(surroundingText1.getText(), is("test"));
+ assertThat(surroundingText1.getSelectionStart(), is(0));
+ assertThat(surroundingText1.getSelectionEnd(), is(0));
+ assertThat(surroundingText1.getOffset(), is(0));
+
+ SurroundingText surroundingText2 = new SurroundingText("", -1, -1, -1);
+ assertThat(surroundingText2.getText(), is(""));
+ assertThat(surroundingText2.getSelectionStart(), is(-1));
+ assertThat(surroundingText2.getSelectionEnd(), is(-1));
+ assertThat(surroundingText2.getOffset(), is(-1));
+
+ SurroundingText surroundingText3 = new SurroundingText("hello", 0, 5, 0);
+ assertThat(surroundingText3.getText(), is("hello"));
+ assertThat(surroundingText3.getSelectionStart(), is(0));
+ assertThat(surroundingText3.getSelectionEnd(), is(5));
+ assertThat(surroundingText3.getOffset(), is(0));
+ }
+
+ @Test
+ public void testSurroundingTextWriteToParcel() {
+ SurroundingText surroundingText = new SurroundingText("text", 0, 1, 2);
+ Parcel parcel = Parcel.obtain();
+ surroundingText.writeToParcel(parcel, 0);
+
+ parcel.setDataPosition(0);
+ SurroundingText surroundingTextFromParcel =
+ SurroundingText.CREATOR.createFromParcel(parcel);
+ assertThat(surroundingText.getText(), is("text"));
+ assertThat(surroundingText.getSelectionStart(), is(0));
+ assertThat(surroundingText.getSelectionEnd(), is(1));
+ assertThat(surroundingText.getOffset(), is(2));
+ assertThat(surroundingTextFromParcel.getText(), is("text"));
+ assertThat(surroundingTextFromParcel.getSelectionStart(), is(0));
+ assertThat(surroundingTextFromParcel.getSelectionEnd(), is(1));
+ assertThat(surroundingTextFromParcel.getOffset(), is(2));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
index 2f21b7f..16e750a 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
@@ -16,7 +16,7 @@
package android.view.textclassifier;
-import static com.google.common.truth.Truth.assertWithMessage;
+import static com.google.common.truth.Truth.assertThat;
import android.provider.DeviceConfig;
@@ -26,73 +26,63 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.function.Consumer;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TextClassificationConstantsTest {
- private static final float EPSILON = 0.0001f;
@Test
- public void testLoadFromDeviceConfig_booleanValue() throws Exception {
- // Saves config original value.
- final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- TextClassificationConstants.LOCAL_TEXT_CLASSIFIER_ENABLED);
-
- final TextClassificationConstants constants = new TextClassificationConstants();
- try {
- // Sets and checks different value.
- setDeviceConfig(TextClassificationConstants.LOCAL_TEXT_CLASSIFIER_ENABLED, "false");
- assertWithMessage(TextClassificationConstants.LOCAL_TEXT_CLASSIFIER_ENABLED)
- .that(constants.isLocalTextClassifierEnabled()).isFalse();
- } finally {
- // Restores config original value.
- setDeviceConfig(TextClassificationConstants.LOCAL_TEXT_CLASSIFIER_ENABLED,
- originalValue);
- }
+ public void booleanSettings() {
+ assertSettings(
+ TextClassificationConstants.LOCAL_TEXT_CLASSIFIER_ENABLED,
+ "false",
+ settings -> assertThat(settings.isLocalTextClassifierEnabled()).isFalse());
}
@Test
- public void testLoadFromDeviceConfig_IntValue() throws Exception {
- // Saves config original value.
- final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH);
-
- final TextClassificationConstants constants = new TextClassificationConstants();
- try {
- // Sets and checks different value.
- setDeviceConfig(TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH, "8");
- assertWithMessage(TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH)
- .that(constants.getGenerateLinksMaxTextLength()).isEqualTo(8);
- } finally {
- // Restores config original value.
- setDeviceConfig(TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH,
- originalValue);
- }
+ public void intSettings() {
+ assertSettings(
+ TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH,
+ "128",
+ settings -> assertThat(settings.getGenerateLinksMaxTextLength()).isEqualTo(128));
}
@Test
- public void testLoadFromDeviceConfig_StringValue() throws Exception {
- // Saves config original value.
- final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- TextClassificationConstants.TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE);
+ public void stringSettings() {
+ assertSettings(
+ TextClassificationConstants.TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE,
+ "com.example.textclassifier",
+ settings -> assertThat(
+ settings.getTextClassifierServicePackageOverride())
+ .isEqualTo("com.example.textclassifier"));
+ }
- final TextClassificationConstants constants = new TextClassificationConstants();
+ @Test
+ public void longSettings() {
+ assertSettings(
+ TextClassificationConstants.SYSTEM_TEXT_CLASSIFIER_API_TIMEOUT_IN_SECOND,
+ "1",
+ settings -> assertThat(
+ settings.getSystemTextClassifierApiTimeoutInSecond())
+ .isEqualTo(1));
+ }
+
+ private static void assertSettings(
+ String key, String value, Consumer<TextClassificationConstants> settingsConsumer) {
+ final String originalValue =
+ DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key);
+ TextClassificationConstants settings = new TextClassificationConstants();
try {
- // Sets and checks different value.
- final String testTextClassifier = "com.example.textclassifier";
- setDeviceConfig(TextClassificationConstants.TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE,
- testTextClassifier);
- assertWithMessage(TextClassificationConstants.TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE)
- .that(constants.getTextClassifierServicePackageOverride()).isEqualTo(
- testTextClassifier);
+ setDeviceConfig(key, value);
+ settingsConsumer.accept(settings);
} finally {
- // Restores config original value.
- setDeviceConfig(TextClassificationConstants.TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE,
- originalValue);
+ setDeviceConfig(key, originalValue);
}
}
- private void setDeviceConfig(String key, String value) {
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key,
- value, /* makeDefault */ false);
+ private static void setDeviceConfig(String key, String value) {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, value, /* makeDefault */ false);
}
}
diff --git a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentCallbackTest.java b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentCallbackTest.java
index 5112326..ef659af6 100644
--- a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentCallbackTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentCallbackTest.java
@@ -66,8 +66,8 @@
* {@link android.widget.cts.TextViewOnReceiveContentCallbackTest}. This class tests some internal
* implementation details, e.g. fallback to the keyboard image API.
*/
-@RunWith(AndroidJUnit4.class)
@MediumTest
+@RunWith(AndroidJUnit4.class)
public class TextViewOnReceiveContentCallbackTest {
private static final Uri SAMPLE_CONTENT_URI = Uri.parse("content://com.example/path");
@@ -101,7 +101,7 @@
// Assert that the callback returns the MIME types declared in the EditorInfo in addition to
// the default.
- assertThat(mDefaultCallback.getSupportedMimeTypes(mEditText)).containsExactly(
+ assertThat(mDefaultCallback.getMimeTypes(mEditText)).containsExactly(
"text/*", "image/gif", "image/png");
}
@@ -118,7 +118,7 @@
onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0));
// Assert that the callback returns the default MIME types.
- assertThat(mDefaultCallback.getSupportedMimeTypes(mEditText)).containsExactly("text/*");
+ assertThat(mDefaultCallback.getMimeTypes(mEditText)).containsExactly("text/*");
}
@Test
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index a4f2065..cc68bb6 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -28,6 +28,7 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -373,6 +374,25 @@
public void removeHdmiCecVolumeControlFeatureListener(
IHdmiCecVolumeControlFeatureListener listener) {
}
+
+ @Override
+ public List<String> getUserCecSettings() {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public List<String> getAllowedCecSettingValues(String name) {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public String getCecSettingValue(String name) {
+ return "";
+ }
+
+ @Override
+ public void setCecSettingValue(String name, String value) {
+ }
}
}
diff --git a/data/etc/car/com.android.car.provision.xml b/data/etc/car/com.android.car.provision.xml
index fa51d55e..37f64b5 100644
--- a/data/etc/car/com.android.car.provision.xml
+++ b/data/etc/car/com.android.car.provision.xml
@@ -16,6 +16,10 @@
-->
<permissions>
<privapp-permissions package="com.android.car.provision">
+ <permission name="android.car.permission.CAR_POWERTRAIN"/>
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.QUERY_ALL_PACKAGES"/>
+ <permission name="android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
<permission name="android.permission.WRITE_SETTINGS"/>
</privapp-permissions>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 977703d..301b491 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -248,8 +248,7 @@
<library name="android.hidl.base-V1.0-java"
file="/system/framework/android.hidl.base-V1.0-java.jar" />
<library name="android.hidl.manager-V1.0-java"
- file="/system/framework/android.hidl.manager-V1.0-java.jar"
- dependency="android.hidl.base-V1.0-java" />
+ file="/system/framework/android.hidl.manager-V1.0-java.jar" />
<!-- These are the standard packages that are white-listed to always have internet
access while in power save mode, even if they aren't in the foreground. -->
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 98c3370..e51bf5e4 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -319,6 +319,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayRotation.java"
},
+ "-1729340764": {
+ "message": "setFinishTaskBounds(%d): bounds=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
+ },
"-1715268616": {
"message": "Last window, removing starting window %s",
"level": "VERBOSE",
@@ -2917,6 +2923,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/TaskDisplayArea.java"
},
+ "1396893178": {
+ "message": "createRootTask unknown displayId=%d",
+ "level": "ERROR",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+ },
"1401295262": {
"message": "Mode default, asking user",
"level": "WARN",
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 8284042..94bfdc9 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -29,7 +29,7 @@
private static native long nativeCreate(String name, long surfaceControl, long width,
long height, boolean tripleBufferingEnabled);
private static native void nativeDestroy(long ptr);
- private static native Surface nativeGetSurface(long ptr);
+ private static native Surface nativeGetSurface(long ptr, boolean includeSurfaceControlHandle);
private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height);
private static native void nativeFlushShadowQueue(long ptr);
@@ -49,7 +49,15 @@
* @return a new Surface instance from the IGraphicsBufferProducer of the adapter.
*/
public Surface createSurface() {
- return nativeGetSurface(mNativeObject);
+ return nativeGetSurface(mNativeObject, false /* includeSurfaceControlHandle */);
+ }
+
+ /**
+ * @return a new Surface instance from the IGraphicsBufferProducer of the adapter and
+ * the SurfaceControl handle.
+ */
+ public Surface createSurfaceWithHandle() {
+ return nativeGetSurface(mNativeObject, true /* includeSurfaceControlHandle */);
}
public void setNextTransaction(SurfaceControl.Transaction t) {
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index 089d6de..864331a 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -18,6 +18,7 @@
import android.annotation.ColorInt;
import android.annotation.ColorLong;
+import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -34,6 +35,10 @@
@UnsupportedAppUsage
private TileMode mTileMode;
+ private final float mFocalX;
+ private final float mFocalY;
+ private final float mFocalRadius;
+
// @ColorInts are replaced by @ColorLongs, but these remain due to @UnsupportedAppUsage.
@UnsupportedAppUsage
@ColorInt
@@ -64,8 +69,8 @@
public RadialGradient(float centerX, float centerY, float radius,
@NonNull @ColorInt int[] colors, @Nullable float[] stops,
@NonNull TileMode tileMode) {
- this(centerX, centerY, radius, convertColors(colors), stops, tileMode,
- ColorSpace.get(ColorSpace.Named.SRGB));
+ this(centerX, centerY, 0f, centerX, centerY, radius, convertColors(colors),
+ stops, tileMode, ColorSpace.get(ColorSpace.Named.SRGB));
}
/**
@@ -88,27 +93,79 @@
public RadialGradient(float centerX, float centerY, float radius,
@NonNull @ColorLong long[] colors, @Nullable float[] stops,
@NonNull TileMode tileMode) {
- this(centerX, centerY, radius, colors.clone(), stops, tileMode, detectColorSpace(colors));
+ this(centerX, centerY, 0f, centerX, centerY, radius, colors.clone(), stops,
+ tileMode, detectColorSpace(colors));
+ }
+
+ /**
+ * Create a shader that draws a radial gradient given the start and end points as well as
+ * starting and ending radii. The starting point is often referred to as the focal center and
+ * represents the starting circle of the radial gradient.
+ *
+ * @param startX The x-coordinate of the center of the starting circle of the radial gradient,
+ * often referred to as the focal point.
+ * @param startY The y-coordinate of the center of the starting circle of the radial gradient,
+ * often referred to as the focal point.
+ * @param startRadius The radius of the starting circle of the radial gradient, often referred
+ * to as the focal radius. Must be greater than or equal to zero.
+ * @param endX The x-coordinate of the center of the radius for the end circle of the
+ * radial gradient
+ * @param endY The y-coordinate of the center of the radius for the end circle of the
+ * radial gradient
+ * @param endRadius The radius of the ending circle for this gradient. This must be strictly
+ * greater than zero. A radius value equal to zero is not allowed.
+ * @param colors The colors to be distributed between the center and edge of the circle
+ * @param stops May be <code>null</code>. Valid values are between <code>0.0f</code> and
+ * <code>1.0f</code>. The relative position of each corresponding color in
+ * the colors array. If <code>null</code>, colors are distributed evenly
+ * between the center and edge of the circle.
+ * @param tileMode The Shader tiling mode
+ *
+ * @throws IllegalArgumentException If one of the following circumstances:
+ * - There are less than two colors
+ * - The colors do not share the same {@link ColorSpace}
+ * - The colors do not use a valid {@link ColorSpace}
+ * - The {@code stops} parameter is not {@code null} and has a different length
+ * from {@code colors}.
+ * - The {@param startRadius} is negative
+ * - The {@param endRadius} is less than or equal to zero
+ */
+ public RadialGradient(float startX, float startY, @FloatRange(from = 0.0f) float startRadius,
+ float endX, float endY, @FloatRange(from = 0.0f, fromInclusive = false) float endRadius,
+ @NonNull @ColorLong long[] colors, @Nullable float[] stops,
+ @NonNull TileMode tileMode) {
+ this(startX, startY, startRadius, endX, endY, endRadius, colors.clone(), stops, tileMode,
+ detectColorSpace(colors));
}
/**
* Base constructor. Assumes @param colors is a copy that this object can hold onto,
* and all colors share @param colorSpace.
*/
- private RadialGradient(float centerX, float centerY, float radius,
- @NonNull @ColorLong long[] colors, @Nullable float[] stops,
- @NonNull TileMode tileMode, ColorSpace colorSpace) {
+ private RadialGradient(float startX, float startY, float startRadius, float endX, float endY,
+ float endRadius, @NonNull @ColorLong long[] colors, @Nullable float[] stops,
+ @NonNull TileMode tileMode, ColorSpace colorSpace
+ ) {
super(colorSpace);
-
- if (radius <= 0) {
- throw new IllegalArgumentException("radius must be > 0");
+ // A focal or starting radius of zero with a focal point that matches the center is
+ // identical to a regular radial gradient
+ if (startRadius < 0) {
+ throw new IllegalArgumentException("starting/focal radius must be >= 0");
}
+
+ if (endRadius <= 0) {
+ throw new IllegalArgumentException("ending radius must be > 0");
+ }
+
if (stops != null && colors.length != stops.length) {
throw new IllegalArgumentException("color and position arrays must be of equal length");
}
- mX = centerX;
- mY = centerY;
- mRadius = radius;
+ mX = endX;
+ mY = endY;
+ mRadius = endRadius;
+ mFocalX = startX;
+ mFocalY = startY;
+ mFocalRadius = startRadius;
mColorLongs = colors;
mPositions = stops != null ? stops.clone() : null;
mTileMode = tileMode;
@@ -150,12 +207,12 @@
/** @hide */
@Override
protected long createNativeInstance(long nativeMatrix) {
- return nativeCreate(nativeMatrix, mX, mY, mRadius,
- mColorLongs, mPositions, mTileMode.nativeInt,
- colorSpace().getNativeInstance());
+ return nativeCreate(nativeMatrix, mFocalX, mFocalY, mFocalRadius, mX, mY, mRadius,
+ mColorLongs, mPositions, mTileMode.nativeInt, colorSpace().getNativeInstance());
}
- private static native long nativeCreate(long matrix, float x, float y, float radius,
- @ColorLong long[] colors, float[] positions, int tileMode, long colorSpaceHandle);
+ private static native long nativeCreate(long matrix, float startX, float startY,
+ float startRadius, float endX, float endY, float endRadius, @ColorLong long[] colors,
+ float[] positions, int tileMode, long colorSpaceHandle);
}
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 90412f4..abf0e8a 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -39,6 +39,7 @@
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
@@ -441,10 +442,11 @@
resPackage = context.getPackageName();
}
if (getResources() == null && !(getResPackage().equals("android"))) {
- final PackageManager pm = context.getPackageManager();
+ final PackageManager pm = context.createContextAsUser(
+ UserHandle.of(userId), /* flags */ 0).getPackageManager();
try {
// assign getResources() as the correct user
- mObj1 = pm.getResourcesForApplicationAsUser(resPackage, userId);
+ mObj1 = pm.getResourcesForApplication(resPackage);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, String.format("Unable to find pkg=%s user=%d",
getResPackage(),
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 6df62c0..a77aec2 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -535,7 +535,8 @@
}
intent.putExtra(EXTRA_ISSUERS, (Serializable) issuersList);
// the PendingIntent is used to get calling package name
- intent.putExtra(EXTRA_SENDER, PendingIntent.getActivity(activity, 0, new Intent(), 0));
+ intent.putExtra(EXTRA_SENDER, PendingIntent.getActivity(activity, 0, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE));
activity.startActivity(intent);
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index cc5286d..9707260 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -177,7 +177,7 @@
&& (keymasterSwEnforcedUserAuthenticators == 0);
boolean userAuthenticationValidWhileOnBody =
keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
- boolean trustedUserPresenceRequred =
+ boolean trustedUserPresenceRequired =
keyCharacteristics.hwEnforced.getBoolean(
KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
@@ -209,7 +209,7 @@
keymasterHwEnforcedUserAuthenticators,
userAuthenticationRequirementEnforcedBySecureHardware,
userAuthenticationValidWhileOnBody,
- trustedUserPresenceRequred,
+ trustedUserPresenceRequired,
invalidatedByBiometricEnrollment,
userConfirmationRequired);
}
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationUtils.java
index 773729e..c82b6e6b 100644
--- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationUtils.java
+++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationUtils.java
@@ -48,7 +48,7 @@
return null;
}
- // An error occured. However, some errors should not lead to init throwing an exception.
+ // An error occurred. However, some errors should not lead to init throwing an exception.
// See below.
InvalidKeyException e =
keyStore.getInvalidKeyException(key.getAlias(), key.getUid(), beginOpResultCode);
diff --git a/libs/WindowManager/Shell/res/layout/global_drop_target.xml b/libs/WindowManager/Shell/res/layout/global_drop_target.xml
new file mode 100644
index 0000000..0dd0b00
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/global_drop_target.xml
@@ -0,0 +1,18 @@
+<?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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
index 02bf385..bee9f41 100644
--- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
+++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
@@ -25,29 +25,77 @@
"group": "WM_SHELL_TRANSITIONS",
"at": "com\/android\/wm\/shell\/Transitions.java"
},
+ "-1382704050": {
+ "message": "Display removed: %d",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_DRAG_AND_DROP",
+ "at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
+ },
"-1340279385": {
"message": "Remove listener=%s",
"level": "VERBOSE",
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
+ "-1325223370": {
+ "message": "Task appeared taskId=%d listener=%s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+ },
"-1312360667": {
"message": "createRootTask() displayId=%d winMode=%d listener=%s",
"level": "VERBOSE",
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
+ "-1006733970": {
+ "message": "Display added: %d",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_DRAG_AND_DROP",
+ "at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
+ },
+ "-1000962629": {
+ "message": "Animate bounds: from=%s to=%s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_DRAG_AND_DROP",
+ "at": "com\/android\/wm\/shell\/draganddrop\/DropOutlineDrawable.java"
+ },
"-880817403": {
"message": "Task vanished taskId=%d",
"level": "VERBOSE",
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
- "-460572385": {
- "message": "Task appeared taskId=%d",
+ "-848099324": {
+ "message": "Letterbox Task Appeared: #%d",
"level": "VERBOSE",
"group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+ "at": "com\/android\/wm\/shell\/LetterboxTaskListener.java"
+ },
+ "-842742255": {
+ "message": "%s onTaskAppeared unknown taskId=%d winMode=%d",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/splitscreen\/SplitScreenTaskListener.java"
+ },
+ "-712674749": {
+ "message": "Clip description: %s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_DRAG_AND_DROP",
+ "at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
+ },
+ "-710770147": {
+ "message": "Add target: %s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_DRAG_AND_DROP",
+ "at": "com\/android\/wm\/shell\/draganddrop\/DragLayout.java"
+ },
+ "-679492476": {
+ "message": "%s onTaskAppeared Primary taskId=%d",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/splitscreen\/SplitScreenTaskListener.java"
},
"-191422040": {
"message": "Transition animations finished, notifying core %s",
@@ -55,12 +103,24 @@
"group": "WM_SHELL_TRANSITIONS",
"at": "com\/android\/wm\/shell\/Transitions.java"
},
+ "154313206": {
+ "message": "%s onTaskAppeared Secondary taskId=%d",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/splitscreen\/SplitScreenTaskListener.java"
+ },
"157713005": {
"message": "Task info changed taskId=%d",
"level": "VERBOSE",
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
+ "274140888": {
+ "message": "Animate alpha: from=%d to=%d",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_DRAG_AND_DROP",
+ "at": "com\/android\/wm\/shell\/draganddrop\/DropOutlineDrawable.java"
+ },
"481673835": {
"message": "addListenerForTaskId taskId=%s",
"level": "VERBOSE",
@@ -85,14 +145,65 @@
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
+ "1104702476": {
+ "message": "Letterbox Task Changed: #%d",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/LetterboxTaskListener.java"
+ },
+ "1184615936": {
+ "message": "Set drop target window visibility: displayId=%d visibility=%d",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_DRAG_AND_DROP",
+ "at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
+ },
+ "1218010718": {
+ "message": "Letterbox Task Vanished: #%d",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/LetterboxTaskListener.java"
+ },
+ "1481772149": {
+ "message": "Current target: %s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_DRAG_AND_DROP",
+ "at": "com\/android\/wm\/shell\/draganddrop\/DragLayout.java"
+ },
+ "1842752748": {
+ "message": "Clip description: handlingDrag=%b mimeTypes=%s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_DRAG_AND_DROP",
+ "at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
+ },
+ "1862198614": {
+ "message": "Drag event: action=%s x=%f y=%f xOffset=%f yOffset=%f",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_DRAG_AND_DROP",
+ "at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
+ },
"1990759023": {
"message": "addListenerForType types=%s listener=%s",
"level": "VERBOSE",
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+ },
+ "2057038970": {
+ "message": "Display changed: %d",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_DRAG_AND_DROP",
+ "at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
+ },
+ "2135461748": {
+ "message": "%s onTaskAppeared Supported",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/splitscreen\/SplitScreenTaskListener.java"
}
},
"groups": {
+ "WM_SHELL_DRAG_AND_DROP": {
+ "tag": "WindowManagerShell"
+ },
"WM_SHELL_TASK_ORG": {
"tag": "WindowManagerShell"
},
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index 6a19083..cc3bf2a 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -22,4 +22,7 @@
<drawable name="forced_resizable_background">#59000000</drawable>
<color name="minimize_dock_shadow_start">#60000000</color>
<color name="minimize_dock_shadow_end">#00000000</color>
+
+ <!-- Background for the various drop targets when handling drag and drop. -->
+ <color name="drop_outline_background">#330000FF</color>
</resources>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index a9917a6..1d85e9f 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -69,4 +69,7 @@
<!-- One-Handed Mode -->
<!-- Threshold for dragging distance to enable one-handed mode -->
<dimen name="gestures_onehanded_drag_threshold">20dp</dimen>
+
+ <!-- The amount to inset the drop target regions from the edge of the display -->
+ <dimen name="drop_layout_display_margin">16dp</dimen>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
index 4cb5fd1..fc0a76e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
@@ -55,8 +55,8 @@
mSyncQueue.runInSync(t -> {
// Reset several properties back to fullscreen (PiP, for example, leaves all these
// properties in a bad state).
- t.setPosition(leash, 0, 0);
t.setWindowCrop(leash, null);
+ t.setPosition(leash, 0, 0);
// TODO(shell-transitions): Eventually set everything in transition so there's no
// SF Transaction here.
if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -92,4 +92,5 @@
public String toString() {
return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN);
}
+
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/LetterboxTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/LetterboxTaskListener.java
new file mode 100644
index 0000000..9010c20
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/LetterboxTaskListener.java
@@ -0,0 +1,110 @@
+/*
+ * 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.wm.shell;
+
+import android.app.ActivityManager;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.SurfaceControl;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+
+/**
+ * Organizes a task in {@link android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN} when
+ * it's presented in the letterbox mode either because orientations of a top activity and a device
+ * don't match or because a top activity is in a size compat mode.
+ */
+final class LetterboxTaskListener implements ShellTaskOrganizer.TaskListener {
+ private static final String TAG = "LetterboxTaskListener";
+
+ private final SyncTransactionQueue mSyncQueue;
+
+ private final SparseArray<SurfaceControl> mLeashByTaskId = new SparseArray<>();
+
+ LetterboxTaskListener(SyncTransactionQueue syncQueue) {
+ mSyncQueue = syncQueue;
+ }
+
+ @Override
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ synchronized (mLeashByTaskId) {
+ if (mLeashByTaskId.get(taskInfo.taskId) != null) {
+ throw new RuntimeException("Task appeared more than once: #" + taskInfo.taskId);
+ }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Letterbox Task Appeared: #%d",
+ taskInfo.taskId);
+ mLeashByTaskId.put(taskInfo.taskId, leash);
+ final Rect taskBounds = taskInfo.getConfiguration().windowConfiguration.getBounds();
+ final Rect activtyBounds = taskInfo.letterboxActivityBounds;
+ final Point taskPositionInParent = taskInfo.positionInParent;
+ mSyncQueue.runInSync(t -> {
+ setPositionAndWindowCrop(
+ t, leash, activtyBounds, taskBounds, taskPositionInParent);
+ if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
+ t.setAlpha(leash, 1f);
+ t.setMatrix(leash, 1, 0, 0, 1);
+ t.show(leash);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ synchronized (mLeashByTaskId) {
+ if (mLeashByTaskId.get(taskInfo.taskId) == null) {
+ Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
+ return;
+ }
+ mLeashByTaskId.remove(taskInfo.taskId);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Letterbox Task Vanished: #%d",
+ taskInfo.taskId);
+ }
+ }
+
+ @Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ synchronized (mLeashByTaskId) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Letterbox Task Changed: #%d",
+ taskInfo.taskId);
+ final SurfaceControl leash = mLeashByTaskId.get(taskInfo.taskId);
+ final Rect taskBounds = taskInfo.getConfiguration().windowConfiguration.getBounds();
+ final Rect activtyBounds = taskInfo.letterboxActivityBounds;
+ final Point taskPositionInParent = taskInfo.positionInParent;
+ mSyncQueue.runInSync(t -> {
+ setPositionAndWindowCrop(
+ t, leash, activtyBounds, taskBounds, taskPositionInParent);
+ });
+ }
+ }
+
+ private static void setPositionAndWindowCrop(
+ SurfaceControl.Transaction transaction,
+ SurfaceControl leash,
+ final Rect activityBounds,
+ final Rect taskBounds,
+ final Point taskPositionInParent) {
+ Rect activtyInTaskCoordinates = new Rect(activityBounds);
+ activtyInTaskCoordinates.offset(-taskBounds.left, -taskBounds.top);
+ transaction.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y);
+ transaction.setWindowCrop(leash, activtyInTaskCoordinates);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellDump.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellDump.java
new file mode 100644
index 0000000..4ba84223
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellDump.java
@@ -0,0 +1,56 @@
+/*
+ * 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.wm.shell;
+
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.draganddrop.DragAndDropController;
+import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.splitscreen.SplitScreen;
+
+import java.io.PrintWriter;
+import java.util.Optional;
+
+/**
+ * An entry point into the shell for dumping shell internal state.
+ */
+public class ShellDump {
+
+ private final Optional<SplitScreen> mSplitScreenOptional;
+ private final Optional<Pip> mPipOptional;
+ private final Optional<OneHanded> mOneHandedOptional;
+ private final ShellTaskOrganizer mShellTaskOrganizer;
+
+ public ShellDump(ShellTaskOrganizer shellTaskOrganizer,
+ Optional<SplitScreen> splitScreenOptional,
+ Optional<Pip> pipOptional,
+ Optional<OneHanded> oneHandedOptional) {
+ mShellTaskOrganizer = shellTaskOrganizer;
+ mSplitScreenOptional = splitScreenOptional;
+ mPipOptional = pipOptional;
+ mOneHandedOptional = oneHandedOptional;
+ }
+
+ public void dump(PrintWriter pw) {
+ mShellTaskOrganizer.dump(pw, "");
+ pw.println();
+ pw.println();
+ mPipOptional.ifPresent(pip -> pip.dump(pw));
+ mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw));
+ mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw));
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java
new file mode 100644
index 0000000..4269a90
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java
@@ -0,0 +1,53 @@
+/*
+ * 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.wm.shell;
+
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.draganddrop.DragAndDropController;
+import com.android.wm.shell.splitscreen.SplitScreen;
+
+import java.util.Optional;
+
+/**
+ * An entry point into the shell for initializing shell internal state.
+ */
+public class ShellInit {
+
+ private final DisplayImeController mDisplayImeController;
+ private final DragAndDropController mDragAndDropController;
+ private final ShellTaskOrganizer mShellTaskOrganizer;
+ private final Optional<SplitScreen> mSplitScreenOptional;
+
+ public ShellInit(DisplayImeController displayImeController,
+ DragAndDropController dragAndDropController,
+ ShellTaskOrganizer shellTaskOrganizer,
+ Optional<SplitScreen> splitScreenOptional) {
+ mDisplayImeController = displayImeController;
+ mDragAndDropController = dragAndDropController;
+ mShellTaskOrganizer = shellTaskOrganizer;
+ mSplitScreenOptional = splitScreenOptional;
+ }
+
+ public void init() {
+ // Start listening for display changes
+ mDisplayImeController.startMonitorDisplays();
+ // Register the shell organizer
+ mShellTaskOrganizer.registerOrganizer();
+ // Bind the splitscreen impl to the drag drop controller
+ mDragAndDropController.setSplitScreenController(mSplitScreenOptional);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 8bd7193..d4ff275 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -20,8 +20,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
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 com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
@@ -29,6 +27,7 @@
import android.annotation.IntDef;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration.WindowingMode;
+import android.os.Binder;
import android.os.IBinder;
import android.util.ArrayMap;
import android.util.Log;
@@ -45,7 +44,6 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -64,14 +62,14 @@
public static final int TASK_LISTENER_TYPE_FULLSCREEN = -2;
public static final int TASK_LISTENER_TYPE_MULTI_WINDOW = -3;
public static final int TASK_LISTENER_TYPE_PIP = -4;
- public static final int TASK_LISTENER_TYPE_SPLIT_SCREEN = -5;
+ public static final int TASK_LISTENER_TYPE_LETTERBOX = -5;
@IntDef(prefix = {"TASK_LISTENER_TYPE_"}, value = {
TASK_LISTENER_TYPE_UNDEFINED,
TASK_LISTENER_TYPE_FULLSCREEN,
TASK_LISTENER_TYPE_MULTI_WINDOW,
TASK_LISTENER_TYPE_PIP,
- TASK_LISTENER_TYPE_SPLIT_SCREEN,
+ TASK_LISTENER_TYPE_LETTERBOX,
})
public @interface TaskListenerType {}
@@ -105,6 +103,8 @@
// TODO(shell-transitions): move to a more "global" Shell location as this isn't only for Tasks
private final Transitions mTransitions;
+ private final Object mLock = new Object();
+
public ShellTaskOrganizer(SyncTransactionQueue syncQueue, TransactionPool transactionPool,
ShellExecutor mainExecutor, ShellExecutor animExecutor) {
this(null, syncQueue, transactionPool, mainExecutor, animExecutor);
@@ -114,76 +114,79 @@
ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController,
SyncTransactionQueue syncQueue, TransactionPool transactionPool,
ShellExecutor mainExecutor, ShellExecutor animExecutor) {
- super(taskOrganizerController);
+ super(taskOrganizerController, mainExecutor);
addListenerForType(new FullscreenTaskListener(syncQueue), TASK_LISTENER_TYPE_FULLSCREEN);
+ addListenerForType(new LetterboxTaskListener(syncQueue), TASK_LISTENER_TYPE_LETTERBOX);
mTransitions = new Transitions(this, transactionPool, mainExecutor, animExecutor);
if (Transitions.ENABLE_SHELL_TRANSITIONS) registerTransitionPlayer(mTransitions);
}
@Override
public List<TaskAppearedInfo> registerOrganizer() {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Registering organizer");
- final List<TaskAppearedInfo> taskInfos = super.registerOrganizer();
- for (int i = 0; i < taskInfos.size(); i++) {
- final TaskAppearedInfo info = taskInfos.get(i);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Existing task: id=%d component=%s",
- info.getTaskInfo().taskId, info.getTaskInfo().baseIntent);
- onTaskAppeared(info);
+ synchronized (mLock) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "Registering organizer");
+ final List<TaskAppearedInfo> taskInfos = super.registerOrganizer();
+ for (int i = 0; i < taskInfos.size(); i++) {
+ final TaskAppearedInfo info = taskInfos.get(i);
+ ProtoLog.v(WM_SHELL_TASK_ORG, "Existing task: id=%d component=%s",
+ info.getTaskInfo().taskId, info.getTaskInfo().baseIntent);
+ onTaskAppeared(info);
+ }
+ return taskInfos;
}
- return taskInfos;
}
- public TaskAppearedInfo createRootTask(
- int displayId, int windowingMode, TaskListener listener) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG,
- "createRootTask() displayId=%d winMode=%d listener=%s",
+ public void createRootTask(int displayId, int windowingMode, TaskListener listener) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "createRootTask() displayId=%d winMode=%d listener=%s",
displayId, windowingMode, listener.toString());
- final TaskAppearedInfo info = super.createRootTask(displayId, windowingMode);
-
- // Add the listener and send the task appeared signal
- mTaskListeners.put(info.getTaskInfo().taskId, listener);
- onTaskAppeared(info);
- return info;
+ final IBinder cookie = new Binder();
+ setPendingLaunchCookieListener(cookie, listener);
+ super.createRootTask(displayId, windowingMode, cookie);
}
/**
* Adds a listener for a specific task id.
*/
public void addListenerForTaskId(TaskListener listener, int taskId) {
- ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForTaskId taskId=%s", taskId);
- if (mTaskListeners.get(taskId) != null) {
- throw new IllegalArgumentException("Listener for taskId=" + taskId + " already exists");
- }
+ synchronized (mLock) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForTaskId taskId=%s", taskId);
+ if (mTaskListeners.get(taskId) != null) {
+ throw new IllegalArgumentException(
+ "Listener for taskId=" + taskId + " already exists");
+ }
- final TaskAppearedInfo info = mTasks.get(taskId);
- if (info == null) {
- throw new IllegalArgumentException("addListenerForTaskId unknown taskId=" + taskId);
- }
+ final TaskAppearedInfo info = mTasks.get(taskId);
+ if (info == null) {
+ throw new IllegalArgumentException("addListenerForTaskId unknown taskId=" + taskId);
+ }
- final TaskListener oldListener = getTaskListener(info.getTaskInfo());
- mTaskListeners.put(taskId, listener);
- updateTaskListenerIfNeeded(info.getTaskInfo(), info.getLeash(), oldListener, listener);
+ final TaskListener oldListener = getTaskListener(info.getTaskInfo());
+ mTaskListeners.put(taskId, listener);
+ updateTaskListenerIfNeeded(info.getTaskInfo(), info.getLeash(), oldListener, listener);
+ }
}
/**
* Adds a listener for tasks with given types.
*/
public void addListenerForType(TaskListener listener, @TaskListenerType int... listenerTypes) {
- ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForType types=%s listener=%s",
- Arrays.toString(listenerTypes), listener);
- for (int listenerType : listenerTypes) {
- if (mTaskListeners.get(listenerType) != null) {
- throw new IllegalArgumentException("Listener for listenerType=" + listenerType
- + " already exists");
- }
- mTaskListeners.put(listenerType, listener);
+ synchronized (mLock) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForType types=%s listener=%s",
+ Arrays.toString(listenerTypes), listener);
+ for (int listenerType : listenerTypes) {
+ if (mTaskListeners.get(listenerType) != null) {
+ throw new IllegalArgumentException("Listener for listenerType=" + listenerType
+ + " already exists");
+ }
+ mTaskListeners.put(listenerType, listener);
- // Notify the listener of all existing tasks with the given type.
- for (int i = mTasks.size() - 1; i >= 0; --i) {
- final TaskAppearedInfo data = mTasks.valueAt(i);
- final TaskListener taskListener = getTaskListener(data.getTaskInfo());
- if (taskListener != listener) continue;
- listener.onTaskAppeared(data.getTaskInfo(), data.getLeash());
+ // Notify the listener of all existing tasks with the given type.
+ for (int i = mTasks.size() - 1; i >= 0; --i) {
+ final TaskAppearedInfo data = mTasks.valueAt(i);
+ final TaskListener taskListener = getTaskListener(data.getTaskInfo());
+ if (taskListener != listener) continue;
+ listener.onTaskAppeared(data.getTaskInfo(), data.getLeash());
+ }
}
}
}
@@ -192,30 +195,32 @@
* Removes a registered listener.
*/
public void removeListener(TaskListener listener) {
- ProtoLog.v(WM_SHELL_TASK_ORG, "Remove listener=%s", listener);
- final int index = mTaskListeners.indexOfValue(listener);
- if (index == -1) {
- Log.w(TAG, "No registered listener found");
- return;
- }
+ synchronized (mLock) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "Remove listener=%s", listener);
+ final int index = mTaskListeners.indexOfValue(listener);
+ if (index == -1) {
+ Log.w(TAG, "No registered listener found");
+ return;
+ }
- // Collect tasks associated with the listener we are about to remove.
- final ArrayList<TaskAppearedInfo> tasks = new ArrayList<>();
- for (int i = mTasks.size() - 1; i >= 0; --i) {
- final TaskAppearedInfo data = mTasks.valueAt(i);
- final TaskListener taskListener = getTaskListener(data.getTaskInfo());
- if (taskListener != listener) continue;
- tasks.add(data);
- }
+ // Collect tasks associated with the listener we are about to remove.
+ final ArrayList<TaskAppearedInfo> tasks = new ArrayList<>();
+ for (int i = mTasks.size() - 1; i >= 0; --i) {
+ final TaskAppearedInfo data = mTasks.valueAt(i);
+ final TaskListener taskListener = getTaskListener(data.getTaskInfo());
+ if (taskListener != listener) continue;
+ tasks.add(data);
+ }
- // Remove listener
- mTaskListeners.removeAt(index);
+ // Remove listener
+ mTaskListeners.removeAt(index);
- // Associate tasks with new listeners if needed.
- for (int i = tasks.size() - 1; i >= 0; --i) {
- final TaskAppearedInfo data = tasks.get(i);
- updateTaskListenerIfNeeded(data.getTaskInfo(), data.getLeash(),
- null /* oldListener already removed*/, getTaskListener(data.getTaskInfo()));
+ // Associate tasks with new listeners if needed.
+ for (int i = tasks.size() - 1; i >= 0; --i) {
+ final TaskAppearedInfo data = tasks.get(i);
+ updateTaskListenerIfNeeded(data.getTaskInfo(), data.getLeash(),
+ null /* oldListener already removed*/, getTaskListener(data.getTaskInfo()));
+ }
}
}
@@ -224,20 +229,24 @@
* appears.
*/
public void setPendingLaunchCookieListener(IBinder cookie, TaskListener listener) {
- mLaunchCookieToListener.put(cookie, listener);
+ synchronized (mLock) {
+ mLaunchCookieToListener.put(cookie, listener);
+ }
}
@Override
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
- onTaskAppeared(new TaskAppearedInfo(taskInfo, leash));
+ synchronized (mLock) {
+ onTaskAppeared(new TaskAppearedInfo(taskInfo, leash));
+ }
}
private void onTaskAppeared(TaskAppearedInfo info) {
final int taskId = info.getTaskInfo().taskId;
- ProtoLog.v(WM_SHELL_TASK_ORG, "Task appeared taskId=%d", taskId);
mTasks.put(taskId, info);
final TaskListener listener =
getTaskListener(info.getTaskInfo(), true /*removeLaunchCookieIfNeeded*/);
+ ProtoLog.v(WM_SHELL_TASK_ORG, "Task appeared taskId=%d listener=%s", taskId, listener);
if (listener != null) {
listener.onTaskAppeared(info.getTaskInfo(), info.getLeash());
}
@@ -245,35 +254,41 @@
@Override
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
- ProtoLog.v(WM_SHELL_TASK_ORG, "Task info changed taskId=%d", taskInfo.taskId);
- final TaskAppearedInfo data = mTasks.get(taskInfo.taskId);
- final TaskListener oldListener = getTaskListener(data.getTaskInfo());
- final TaskListener newListener = getTaskListener(taskInfo);
- mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, data.getLeash()));
- final boolean updated = updateTaskListenerIfNeeded(
- taskInfo, data.getLeash(), oldListener, newListener);
- if (!updated && newListener != null) {
- newListener.onTaskInfoChanged(taskInfo);
+ synchronized (mLock) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "Task info changed taskId=%d", taskInfo.taskId);
+ final TaskAppearedInfo data = mTasks.get(taskInfo.taskId);
+ final TaskListener oldListener = getTaskListener(data.getTaskInfo());
+ final TaskListener newListener = getTaskListener(taskInfo);
+ mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, data.getLeash()));
+ final boolean updated = updateTaskListenerIfNeeded(
+ taskInfo, data.getLeash(), oldListener, newListener);
+ if (!updated && newListener != null) {
+ newListener.onTaskInfoChanged(taskInfo);
+ }
}
}
@Override
public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
- ProtoLog.v(WM_SHELL_TASK_ORG, "Task root back pressed taskId=%d", taskInfo.taskId);
- final TaskListener listener = getTaskListener(taskInfo);
- if (listener != null) {
- listener.onBackPressedOnTaskRoot(taskInfo);
+ synchronized (mLock) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "Task root back pressed taskId=%d", taskInfo.taskId);
+ final TaskListener listener = getTaskListener(taskInfo);
+ if (listener != null) {
+ listener.onBackPressedOnTaskRoot(taskInfo);
+ }
}
}
@Override
public void onTaskVanished(RunningTaskInfo taskInfo) {
- ProtoLog.v(WM_SHELL_TASK_ORG, "Task vanished taskId=%d", taskInfo.taskId);
- final int taskId = taskInfo.taskId;
- final TaskListener listener = getTaskListener(mTasks.get(taskId).getTaskInfo());
- mTasks.remove(taskId);
- if (listener != null) {
- listener.onTaskVanished(taskInfo);
+ synchronized (mLock) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "Task vanished taskId=%d", taskInfo.taskId);
+ final int taskId = taskInfo.taskId;
+ final TaskListener listener = getTaskListener(mTasks.get(taskId).getTaskInfo());
+ mTasks.remove(taskId);
+ if (listener != null) {
+ listener.onTaskVanished(taskInfo);
+ }
}
}
@@ -321,26 +336,25 @@
if (listener != null) return listener;
// Next we try type specific listeners.
- final int windowingMode = getWindowingMode(runningTaskInfo);
- final int taskListenerType = windowingModeToTaskListenerType(windowingMode);
+ final int taskListenerType = taskInfoToTaskListenerType(runningTaskInfo);
return mTaskListeners.get(taskListenerType);
}
@WindowingMode
- private static int getWindowingMode(RunningTaskInfo taskInfo) {
+ public static int getWindowingMode(RunningTaskInfo taskInfo) {
return taskInfo.configuration.windowConfiguration.getWindowingMode();
}
- private static @TaskListenerType int windowingModeToTaskListenerType(
- @WindowingMode int windowingMode) {
+ @VisibleForTesting
+ static @TaskListenerType int taskInfoToTaskListenerType(RunningTaskInfo runningTaskInfo) {
+ final int windowingMode = getWindowingMode(runningTaskInfo);
switch (windowingMode) {
case WINDOWING_MODE_FULLSCREEN:
- return TASK_LISTENER_TYPE_FULLSCREEN;
+ return runningTaskInfo.letterboxActivityBounds != null
+ ? TASK_LISTENER_TYPE_LETTERBOX
+ : TASK_LISTENER_TYPE_FULLSCREEN;
case WINDOWING_MODE_MULTI_WINDOW:
return TASK_LISTENER_TYPE_MULTI_WINDOW;
- case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
- case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
- return TASK_LISTENER_TYPE_SPLIT_SCREEN;
case WINDOWING_MODE_PINNED:
return TASK_LISTENER_TYPE_PIP;
case WINDOWING_MODE_FREEFORM:
@@ -354,10 +368,10 @@
switch (type) {
case TASK_LISTENER_TYPE_FULLSCREEN:
return "TASK_LISTENER_TYPE_FULLSCREEN";
+ case TASK_LISTENER_TYPE_LETTERBOX:
+ return "TASK_LISTENER_TYPE_LETTERBOX";
case TASK_LISTENER_TYPE_MULTI_WINDOW:
return "TASK_LISTENER_TYPE_MULTI_WINDOW";
- case TASK_LISTENER_TYPE_SPLIT_SCREEN:
- return "TASK_LISTENER_TYPE_SPLIT_SCREEN";
case TASK_LISTENER_TYPE_PIP:
return "TASK_LISTENER_TYPE_PIP";
case TASK_LISTENER_TYPE_UNDEFINED:
@@ -368,32 +382,34 @@
}
public void dump(@NonNull PrintWriter pw, String prefix) {
- final String innerPrefix = prefix + " ";
- final String childPrefix = innerPrefix + " ";
- pw.println(prefix + TAG);
- pw.println(innerPrefix + mTaskListeners.size() + " Listeners");
- for (int i = mTaskListeners.size() - 1; i >= 0; --i) {
- final int key = mTaskListeners.keyAt(i);
- final TaskListener listener = mTaskListeners.valueAt(i);
- pw.println(innerPrefix + "#" + i + " " + taskListenerTypeToString(key));
- listener.dump(pw, childPrefix);
- }
+ synchronized (mLock) {
+ final String innerPrefix = prefix + " ";
+ final String childPrefix = innerPrefix + " ";
+ pw.println(prefix + TAG);
+ pw.println(innerPrefix + mTaskListeners.size() + " Listeners");
+ for (int i = mTaskListeners.size() - 1; i >= 0; --i) {
+ final int key = mTaskListeners.keyAt(i);
+ final TaskListener listener = mTaskListeners.valueAt(i);
+ pw.println(innerPrefix + "#" + i + " " + taskListenerTypeToString(key));
+ listener.dump(pw, childPrefix);
+ }
- pw.println();
- pw.println(innerPrefix + mTasks.size() + " Tasks");
- for (int i = mTasks.size() - 1; i >= 0; --i) {
- final int key = mTasks.keyAt(i);
- final TaskAppearedInfo info = mTasks.valueAt(i);
- final TaskListener listener = getTaskListener(info.getTaskInfo());
- pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener);
- }
+ pw.println();
+ pw.println(innerPrefix + mTasks.size() + " Tasks");
+ for (int i = mTasks.size() - 1; i >= 0; --i) {
+ final int key = mTasks.keyAt(i);
+ final TaskAppearedInfo info = mTasks.valueAt(i);
+ final TaskListener listener = getTaskListener(info.getTaskInfo());
+ pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener);
+ }
- pw.println();
- pw.println(innerPrefix + mLaunchCookieToListener.size() + " Launch Cookies");
- for (int i = mLaunchCookieToListener.size() - 1; i >= 0; --i) {
- final IBinder key = mLaunchCookieToListener.keyAt(i);
- final TaskListener listener = mLaunchCookieToListener.valueAt(i);
- pw.println(innerPrefix + "#" + i + " cookie=" + key + " listener=" + listener);
+ pw.println();
+ pw.println(innerPrefix + mLaunchCookieToListener.size() + " Launch Cookies");
+ for (int i = mLaunchCookieToListener.size() - 1; i >= 0; --i) {
+ final IBinder key = mLaunchCookieToListener.keyAt(i);
+ final TaskListener listener = mLaunchCookieToListener.valueAt(i);
+ pw.println(innerPrefix + "#" + i + " cookie=" + key + " listener=" + listener);
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java
index 36e49d9..04be3b7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java
@@ -109,13 +109,13 @@
mAnimExecutor.execute(va::start);
}
- private static boolean isOpeningType(@WindowManager.TransitionType int legacyType) {
+ private static boolean isOpeningType(@WindowManager.TransitionOldType int legacyType) {
// TODO(shell-transitions): consider providing and using z-order vs the global type for
// this determination.
- return legacyType == WindowManager.TRANSIT_TASK_OPEN
- || legacyType == WindowManager.TRANSIT_TASK_TO_FRONT
- || legacyType == WindowManager.TRANSIT_TASK_OPEN_BEHIND
- || legacyType == WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+ return legacyType == WindowManager.TRANSIT_OLD_TASK_OPEN
+ || legacyType == WindowManager.TRANSIT_OLD_TASK_TO_FRONT
+ || legacyType == WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND
+ || legacyType == WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
index 416ada7..a3b720c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
@@ -17,12 +17,16 @@
package com.android.wm.shell.animation;
import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
import android.view.animation.PathInterpolator;
/**
* Common interpolators used in wm shell library.
*/
public class Interpolators {
+
+ public static final Interpolator LINEAR = new LinearInterpolator();
+
/**
* Interpolator for alpha in animation.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
new file mode 100644
index 0000000..bf5b1d8
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -0,0 +1,303 @@
+/*
+ * 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.wm.shell.draganddrop;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.content.ClipDescription.EXTRA_ACTIVITY_OPTIONS;
+import static android.content.ClipDescription.EXTRA_PENDING_INTENT;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
+import static android.content.Intent.EXTRA_PACKAGE_NAME;
+import static android.content.Intent.EXTRA_SHORTCUT_ID;
+import static android.content.Intent.EXTRA_TASK_ID;
+import static android.content.Intent.EXTRA_USER;
+import static android.view.DragEvent.ACTION_DRAG_ENDED;
+import static android.view.DragEvent.ACTION_DRAG_ENTERED;
+import static android.view.DragEvent.ACTION_DRAG_EXITED;
+import static android.view.DragEvent.ACTION_DRAG_LOCATION;
+import static android.view.DragEvent.ACTION_DRAG_STARTED;
+import static android.view.DragEvent.ACTION_DROP;
+import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.DragEvent;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.splitscreen.SplitScreen;
+
+import java.util.Optional;
+
+/**
+ * Handles the global drag and drop handling for the Shell.
+ */
+public class DragAndDropController implements DisplayController.OnDisplaysChangedListener,
+ View.OnDragListener {
+
+ private static final String TAG = DragAndDropController.class.getSimpleName();
+
+ private final Context mContext;
+ private final DisplayController mDisplayController;
+ private SplitScreen mSplitScreen;
+
+ private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>();
+ private boolean mIsHandlingDrag;
+ private DragLayout mDragLayout;
+ private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+
+ public DragAndDropController(Context context, DisplayController displayController) {
+ mContext = context;
+ mDisplayController = displayController;
+ mDisplayController.addDisplayWindowListener(this);
+ }
+
+ public void setSplitScreenController(Optional<SplitScreen> splitscreen) {
+ mSplitScreen = splitscreen.orElse(null);
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Display added: %d", displayId);
+ final Context context = mDisplayController.getDisplayContext(displayId);
+ final WindowManager wm = context.getSystemService(WindowManager.class);
+
+ // TODO(b/169894807): Figure out the right layer for this, needs to be below the task bar
+ final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
+ TYPE_APPLICATION_OVERLAY,
+ FLAG_NOT_FOCUSABLE | FLAG_HARDWARE_ACCELERATED,
+ PixelFormat.TRANSLUCENT);
+ layoutParams.privateFlags |= SYSTEM_FLAG_SHOW_FOR_ALL_USERS
+ | PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP
+ | PRIVATE_FLAG_NO_MOVE_ANIMATION;
+ layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ layoutParams.setFitInsetsTypes(0);
+ layoutParams.setTitle("ShellDropTarget");
+
+ FrameLayout dropTarget = (FrameLayout) LayoutInflater.from(context).inflate(
+ R.layout.global_drop_target, null);
+ dropTarget.setOnDragListener(this);
+ dropTarget.setVisibility(View.INVISIBLE);
+ wm.addView(dropTarget, layoutParams);
+ mDisplayDropTargets.put(displayId, new PerDisplay(displayId, context, wm, dropTarget));
+ }
+
+ @Override
+ public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Display changed: %d", displayId);
+ final PerDisplay pd = mDisplayDropTargets.get(displayId);
+ pd.dropTarget.requestApplyInsets();
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Display removed: %d", displayId);
+ final PerDisplay pd = mDisplayDropTargets.get(displayId);
+ pd.wm.removeViewImmediate(pd.dropTarget);
+ mDisplayDropTargets.remove(displayId);
+ }
+
+ @Override
+ public boolean onDrag(View target, DragEvent event) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "Drag event: action=%s x=%f y=%f xOffset=%f yOffset=%f",
+ DragEvent.actionToString(event.getAction()), event.getX(), event.getY(),
+ event.getOffsetX(), event.getOffsetY());
+ final int displayId = target.getDisplay().getDisplayId();
+ final PerDisplay pd = mDisplayDropTargets.get(displayId);
+ final ClipDescription description = event.getClipDescription();
+
+ if (event.getAction() == ACTION_DRAG_STARTED) {
+ final boolean hasValidClipData = description.hasMimeType(MIMETYPE_APPLICATION_ACTIVITY)
+ || description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT)
+ || description.hasMimeType(MIMETYPE_APPLICATION_TASK);
+ mIsHandlingDrag = hasValidClipData;
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "Clip description: handlingDrag=%b mimeTypes=%s",
+ mIsHandlingDrag, getMimeTypes(description));
+ }
+
+ if (!mIsHandlingDrag) {
+ return false;
+ }
+
+ switch (event.getAction()) {
+ case ACTION_DRAG_STARTED:
+ mDragLayout = new DragLayout(pd.context,
+ mDisplayController.getDisplayLayout(displayId), mSplitScreen);
+ pd.dropTarget.addView(mDragLayout,
+ new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+ setDropTargetWindowVisibility(pd, View.VISIBLE);
+ break;
+ case ACTION_DRAG_ENTERED:
+ mDragLayout.show(event);
+ break;
+ case ACTION_DRAG_LOCATION:
+ mDragLayout.update(event);
+ break;
+ case ACTION_DROP: {
+ return handleDrop(event, pd);
+ }
+ case ACTION_DRAG_EXITED: {
+ // Either one of DROP or EXITED will happen, and when EXITED we won't consume
+ // the drag surface
+ mDragLayout.hide(event, null);
+ break;
+ }
+ case ACTION_DRAG_ENDED:
+ // TODO(b/169894807): Ensure sure it's not possible to get ENDED without DROP
+ // or EXITED
+ if (!mDragLayout.hasDropped()) {
+ final View dragLayout = mDragLayout;
+ mDragLayout.hide(event, () -> {
+ setDropTargetWindowVisibility(pd, View.INVISIBLE);
+ pd.dropTarget.removeView(dragLayout);
+ });
+ }
+ mDragLayout = null;
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * Handles dropping on the drop target.
+ */
+ private boolean handleDrop(DragEvent event, PerDisplay pd) {
+ final ClipData data = event.getClipData();
+ final ClipDescription description = event.getClipDescription();
+ final SurfaceControl dragSurface = event.getDragSurface();
+ final View dragLayout = mDragLayout;
+ final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
+ final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
+ return mDragLayout.drop(event, dragSurface, (dropTargetBounds) -> {
+ if (dropTargetBounds != null && data.getItemCount() > 0) {
+ final Intent intent = data.getItemAt(0).getIntent();
+ // TODO(b/169894807): Properly handle the drop, for now just launch it
+ if (isTask) {
+ int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
+ try {
+ ActivityTaskManager.getService().startActivityFromRecents(
+ taskId, null);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to launch task", e);
+ }
+ } else if (isShortcut) {
+ try {
+ Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
+ ? intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS)
+ : null;
+ LauncherApps launcherApps =
+ mContext.getSystemService(LauncherApps.class);
+ launcherApps.startShortcut(
+ intent.getStringExtra(EXTRA_PACKAGE_NAME),
+ intent.getStringExtra(EXTRA_SHORTCUT_ID),
+ null /* sourceBounds */, opts,
+ intent.getParcelableExtra(EXTRA_USER));
+ } catch (ActivityNotFoundException e) {
+ Slog.e(TAG, "Failed to launch shortcut", e);
+ }
+ } else {
+ PendingIntent pi = intent.getParcelableExtra(EXTRA_PENDING_INTENT);
+ try {
+ pi.send();
+ } catch (PendingIntent.CanceledException e) {
+ Slog.e(TAG, "Failed to launch activity", e);
+ }
+ }
+ }
+
+ setDropTargetWindowVisibility(pd, View.INVISIBLE);
+ pd.dropTarget.removeView(dragLayout);
+
+ // Clean up the drag surface
+ mTransaction.reparent(dragSurface, null);
+ mTransaction.apply();
+ });
+ }
+
+ private void setDropTargetWindowVisibility(PerDisplay pd, int visibility) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "Set drop target window visibility: displayId=%d visibility=%d",
+ pd.displayId, visibility);
+ pd.dropTarget.setVisibility(visibility);
+ if (visibility == View.VISIBLE) {
+ pd.dropTarget.requestApplyInsets();
+ }
+ }
+
+ private String getMimeTypes(ClipDescription description) {
+ String mimeTypes = "";
+ for (int i = 0; i < description.getMimeTypeCount(); i++) {
+ if (i > 0) {
+ mimeTypes += ", ";
+ }
+ mimeTypes += description.getMimeType(i);
+ }
+ return mimeTypes;
+ }
+
+ private static class PerDisplay {
+ final int displayId;
+ final Context context;
+ final WindowManager wm;
+ final FrameLayout dropTarget;
+
+ PerDisplay(int dispId, Context c, WindowManager w, FrameLayout l) {
+ displayId = dispId;
+ context = c;
+ wm = w;
+ dropTarget = l;
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
new file mode 100644
index 0000000..b70036b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -0,0 +1,248 @@
+/*
+ * 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.wm.shell.draganddrop;
+
+import static com.android.wm.shell.animation.Interpolators.FAST_OUT_LINEAR_IN;
+import static com.android.wm.shell.animation.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.wm.shell.animation.Interpolators.LINEAR;
+import static com.android.wm.shell.animation.Interpolators.LINEAR_OUT_SLOW_IN;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.view.DragEvent;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowInsets.Type;
+import android.view.animation.Interpolator;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.splitscreen.SplitScreen;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Coordinates the visible drop targets for the current drag.
+ */
+public class DragLayout extends View {
+
+ private final DisplayLayout mDisplayLayout;
+ private final SplitScreen mSplitScreen;
+
+ private final ArrayList<Target> mTargets = new ArrayList<>();
+ private Target mCurrentTarget = null;
+ private DropOutlineDrawable mDropOutline;
+ private int mDisplayMargin;
+ private Insets mInsets = Insets.NONE;
+ private boolean mHasDropped;
+
+ public DragLayout(Context context, DisplayLayout displayLayout, SplitScreen splitscreen) {
+ super(context);
+ mDisplayLayout = displayLayout;
+ mSplitScreen = splitscreen;
+ mDisplayMargin = context.getResources().getDimensionPixelSize(
+ R.dimen.drop_layout_display_margin);
+ mDropOutline = new DropOutlineDrawable(context);
+ setBackground(mDropOutline);
+ setWillNotDraw(false);
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ mInsets = insets.getInsets(Type.systemBars() | Type.displayCutout());
+ calculateDropTargets();
+ return super.onApplyWindowInsets(insets);
+ }
+
+ @Override
+ protected boolean verifyDrawable(@NonNull Drawable who) {
+ return who == mDropOutline || super.verifyDrawable(who);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mCurrentTarget != null) {
+ mDropOutline.draw(canvas);
+ }
+ }
+
+ public boolean hasDropTarget() {
+ return mCurrentTarget != null;
+ }
+
+ public boolean hasDropped() {
+ return mHasDropped;
+ }
+
+ public void show(DragEvent event) {
+ calculateDropTargets();
+ mHasDropped = false;
+ }
+
+ private void calculateDropTargets() {
+ // Calculate all the regions based on split and landscape portrait
+ // TODO: Filter based on clip data
+ final float SIDE_MARGIN_PCT = 0.3f;
+ final int w = mDisplayLayout.width();
+ final int h = mDisplayLayout.height();
+ final int iw = w - mInsets.left - mInsets.right;
+ final int ih = h - mInsets.top - mInsets.bottom;
+ final int l = mInsets.left;
+ final int t = mInsets.top;
+ final int r = mInsets.right;
+ final int b = mInsets.bottom;
+ mTargets.clear();
+
+ // Left split
+ addTarget(new Target(
+ new Rect(0, 0,
+ (int) (w * SIDE_MARGIN_PCT), h),
+ new Rect(l + mDisplayMargin, t + mDisplayMargin,
+ l + iw / 2 - mDisplayMargin, t + ih - mDisplayMargin),
+ new Rect(0, 0, w / 2, h)));
+
+ // Fullscreen
+ addTarget(new Target(
+ new Rect((int) (w * SIDE_MARGIN_PCT), 0,
+ w - (int) (w * SIDE_MARGIN_PCT), h),
+ new Rect(l + mDisplayMargin, t + mDisplayMargin,
+ l + iw - mDisplayMargin, t + ih - mDisplayMargin),
+ new Rect(0, 0, w, h)));
+
+ // Right split
+ addTarget(new Target(
+ new Rect(w - (int) (w * SIDE_MARGIN_PCT), 0,
+ w, h),
+ new Rect(l + iw / 2 + mDisplayMargin, t + mDisplayMargin,
+ l + iw - mDisplayMargin, t + ih - mDisplayMargin),
+ new Rect(w / 2, 0, w, h)));
+ }
+
+ private void addTarget(Target t) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Add target: %s", t);
+ mTargets.add(t);
+ }
+
+ public void update(DragEvent event) {
+ // Find containing region, if the same as mCurrentRegion, then skip, otherwise, animate the
+ // visibility of the current region
+ Target target = null;
+ for (int i = mTargets.size() - 1; i >= 0; i--) {
+ Target t = mTargets.get(i);
+ if (t.hitRegion.contains((int) event.getX(), (int) event.getY())) {
+ target = t;
+ break;
+ }
+ }
+ if (target != null && mCurrentTarget != target) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Current target: %s", target);
+ Interpolator boundsInterpolator = FAST_OUT_SLOW_IN;
+ if (mCurrentTarget == null) {
+ mDropOutline.startVisibilityAnimation(true, LINEAR);
+ Rect initialBounds = new Rect(target.drawRegion);
+ initialBounds.inset(mDisplayMargin, mDisplayMargin);
+ mDropOutline.setRegionBounds(initialBounds);
+ boundsInterpolator = LINEAR_OUT_SLOW_IN;
+ }
+ mDropOutline.startBoundsAnimation(target.drawRegion, boundsInterpolator);
+ mCurrentTarget = target;
+ }
+ }
+
+ public void hide(DragEvent event, Runnable hideCompleteCallback) {
+ ObjectAnimator alphaAnimator = mDropOutline.startVisibilityAnimation(false, LINEAR);
+ ObjectAnimator boundsAnimator = null;
+ if (mCurrentTarget != null) {
+ Rect finalBounds = new Rect(mCurrentTarget.drawRegion);
+ finalBounds.inset(mDisplayMargin, mDisplayMargin);
+ boundsAnimator = mDropOutline.startBoundsAnimation(finalBounds, FAST_OUT_LINEAR_IN);
+ }
+
+ if (hideCompleteCallback != null) {
+ ObjectAnimator lastAnim = boundsAnimator != null
+ ? boundsAnimator
+ : alphaAnimator;
+ lastAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ hideCompleteCallback.run();
+ }
+ });
+ }
+
+ mCurrentTarget = null;
+ }
+
+ public boolean drop(DragEvent event, SurfaceControl dragSurface,
+ Consumer<Rect> dropCompleteCallback) {
+ mHasDropped = true;
+ // TODO(b/169894807): Coordinate with dragSurface
+ final Rect dropRegion = mCurrentTarget != null
+ ? mCurrentTarget.dropTargetBounds
+ : null;
+
+ ObjectAnimator alphaAnimator = mDropOutline.startVisibilityAnimation(false, LINEAR);
+ ObjectAnimator boundsAnimator = null;
+ if (dropRegion != null) {
+ Rect finalBounds = new Rect(mCurrentTarget.drawRegion);
+ finalBounds.inset(mDisplayMargin, mDisplayMargin);
+ mDropOutline.startBoundsAnimation(finalBounds, FAST_OUT_LINEAR_IN);
+ }
+
+ if (dropCompleteCallback != null) {
+ ObjectAnimator lastAnim = boundsAnimator != null
+ ? boundsAnimator
+ : alphaAnimator;
+ lastAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ dropCompleteCallback.accept(dropRegion);
+ }
+ });
+ }
+ return dropRegion != null;
+ }
+
+ private static class Target {
+ final Rect hitRegion;
+ final Rect drawRegion;
+ final Rect dropTargetBounds;
+
+ public Target(Rect hit, Rect draw, Rect drop) {
+ hitRegion = hit;
+ drawRegion = draw;
+ dropTargetBounds = drop;
+ }
+
+ @Override
+ public String toString() {
+ return "Target {hit=" + hitRegion + " drop=" + dropTargetBounds + "}";
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropOutlineDrawable.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropOutlineDrawable.java
new file mode 100644
index 0000000..08edc2f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropOutlineDrawable.java
@@ -0,0 +1,165 @@
+/*
+ * 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.wm.shell.draganddrop;
+
+import android.animation.ObjectAnimator;
+import android.animation.RectEvaluator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.IntProperty;
+import android.util.Property;
+import android.view.animation.Interpolator;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.graphics.ColorUtils;
+import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.R;
+
+/**
+ * Drawable to draw the region of the
+ */
+public class DropOutlineDrawable extends Drawable {
+
+ private static final int BOUNDS_DURATION = 200;
+ private static final int ALPHA_DURATION = 135;
+
+ private final IntProperty<DropOutlineDrawable> ALPHA =
+ new IntProperty<DropOutlineDrawable>("alpha") {
+ @Override
+ public void setValue(DropOutlineDrawable d, int alpha) {
+ d.setAlpha(alpha);
+ }
+
+ @Override
+ public Integer get(DropOutlineDrawable d) {
+ return d.getAlpha();
+ }
+ };
+
+ private final Property<DropOutlineDrawable, Rect> BOUNDS =
+ new Property<DropOutlineDrawable, Rect>(Rect.class, "bounds") {
+ @Override
+ public void set(DropOutlineDrawable d, Rect bounds) {
+ d.setRegionBounds(bounds);
+ }
+
+ @Override
+ public Rect get(DropOutlineDrawable d) {
+ return d.getRegionBounds();
+ }
+ };
+
+ private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect());
+ private ObjectAnimator mBoundsAnimator;
+ private ObjectAnimator mAlphaAnimator;
+
+ private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final Rect mBounds = new Rect();
+ private final float mCornerRadius;
+ private final int mMaxAlpha;
+ private int mColor;
+
+ public DropOutlineDrawable(Context context) {
+ super();
+ // TODO(b/169894807): Use corner specific radii and maybe lower radius for non-edge corners
+ mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context.getResources());
+ mColor = context.getColor(R.color.drop_outline_background);
+ mMaxAlpha = Color.alpha(mColor);
+ // Initialize as hidden
+ ALPHA.set(this, 0);
+ }
+
+ @Override
+ public void setColorFilter(@Nullable ColorFilter colorFilter) {
+ // Do nothing
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ mColor = ColorUtils.setAlphaComponent(mColor, alpha);
+ mPaint.setColor(mColor);
+ invalidateSelf();
+ }
+
+ @Override
+ public int getAlpha() {
+ return Color.alpha(mColor);
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ invalidateSelf();
+ }
+
+ @Override
+ public void draw(@NonNull Canvas canvas) {
+ canvas.drawRoundRect(mBounds.left, mBounds.top, mBounds.right, mBounds.bottom,
+ mCornerRadius, mCornerRadius, mPaint);
+ }
+
+ public void setRegionBounds(Rect bounds) {
+ mBounds.set(bounds);
+ invalidateSelf();
+ }
+
+ public Rect getRegionBounds() {
+ return mBounds;
+ }
+
+ ObjectAnimator startBoundsAnimation(Rect toBounds, Interpolator interpolator) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Animate bounds: from=%s to=%s",
+ mBounds, toBounds);
+ if (mBoundsAnimator != null) {
+ mBoundsAnimator.cancel();
+ }
+ mBoundsAnimator = ObjectAnimator.ofObject(this, BOUNDS, mRectEvaluator,
+ mBounds, toBounds);
+ mBoundsAnimator.setDuration(BOUNDS_DURATION);
+ mBoundsAnimator.setInterpolator(interpolator);
+ mBoundsAnimator.start();
+ return mBoundsAnimator;
+ }
+
+ ObjectAnimator startVisibilityAnimation(boolean visible, Interpolator interpolator) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Animate alpha: from=%d to=%d",
+ Color.alpha(mColor), visible ? mMaxAlpha : 0);
+ if (mAlphaAnimator != null) {
+ mAlphaAnimator.cancel();
+ }
+ mAlphaAnimator = ObjectAnimator.ofInt(this, ALPHA, Color.alpha(mColor),
+ visible ? mMaxAlpha : 0);
+ mAlphaAnimator.setDuration(ALPHA_DURATION);
+ mAlphaAnimator.setInterpolator(interpolator);
+ mAlphaAnimator.start();
+ return mAlphaAnimator;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index 3ded409..59c79ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -22,7 +22,6 @@
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
-import android.media.session.MediaController;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.pip.tv.PipController;
@@ -35,13 +34,6 @@
*/
public interface Pip {
/**
- * Registers {@link com.android.wm.shell.pip.tv.PipController.Listener} that gets called.
- * whenever receiving notification on changes in PIP.
- */
- default void addListener(PipController.Listener listener) {
- }
-
- /**
* Registers a {@link PipController.MediaListener} to PipController.
*/
default void addMediaListener(PipController.MediaListener listener) {
@@ -68,17 +60,8 @@
}
/**
- * Get current play back state. (e.g: Used in TV)
- *
- * @return The state of defined in PipController.
- */
- default int getPlaybackState() {
- return -1;
- }
-
- /**
* Get the touch handler which manages all the touch handling for PIP on the Phone,
- * including moving, dismissing and expanding the PIP. (Do not used in TV)
+ * including moving, dismissing and expanding the PIP. (Do not use in TV)
*
* @return
*/
@@ -87,15 +70,6 @@
}
/**
- * Get MediaController.
- *
- * @return The MediaController instance.
- */
- default MediaController getMediaController() {
- return null;
- }
-
- /**
* Hides the PIP menu.
*/
default void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {}
@@ -171,12 +145,6 @@
}
/**
- * Removes a {@link PipController.Listener} from PipController.
- */
- default void removeListener(PipController.Listener listener) {
- }
-
- /**
* Removes a {@link PipController.MediaListener} from PipController.
*/
default void removeMediaListener(PipController.MediaListener listener) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java
index 6d6cc20..4a9e28e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java
@@ -34,13 +34,10 @@
import android.util.Log;
import android.util.Size;
import android.util.TypedValue;
-import android.view.Display;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.window.WindowContainerTransaction;
-import com.android.wm.shell.common.DisplayLayout;
-
import java.io.PrintWriter;
/**
@@ -54,13 +51,10 @@
private final @NonNull PipBoundsState mPipBoundsState;
private final PipSnapAlgorithm mSnapAlgorithm;
- private final DisplayInfo mDisplayInfo = new DisplayInfo();
- private DisplayLayout mDisplayLayout;
private float mDefaultAspectRatio;
private float mMinAspectRatio;
private float mMaxAspectRatio;
- private float mAspectRatio;
private int mDefaultStackGravity;
private int mDefaultMinSize;
private Point mScreenEdgeInsets;
@@ -75,12 +69,11 @@
public PipBoundsHandler(Context context, @NonNull PipBoundsState pipBoundsState) {
mPipBoundsState = pipBoundsState;
mSnapAlgorithm = new PipSnapAlgorithm(context);
- mDisplayLayout = new DisplayLayout();
reloadResources(context);
// Initialize the aspect ratio to the default aspect ratio. Don't do this in reload
// resources as it would clobber mAspectRatio when entering PiP from fullscreen which
// triggers a configuration change and the resources to be reloaded.
- mAspectRatio = mDefaultAspectRatio;
+ mPipBoundsState.setAspectRatio(mDefaultAspectRatio);
}
/**
@@ -110,22 +103,6 @@
}
/**
- * Sets or update latest {@link DisplayLayout} when new display added or rotation callbacks
- * from {@link DisplayController.OnDisplaysChangedListener}
- * @param newDisplayLayout latest {@link DisplayLayout}
- */
- public void setDisplayLayout(DisplayLayout newDisplayLayout) {
- mDisplayLayout.set(newDisplayLayout);
- }
-
- /**
- * Get the current saved display info.
- */
- public DisplayInfo getDisplayInfo() {
- return mDisplayInfo;
- }
-
- /**
* Update the Min edge size for {@link PipSnapAlgorithm} to calculate corresponding bounds
* @param minEdgeSize
*/
@@ -133,10 +110,6 @@
mCurrentMinSize = minEdgeSize;
}
- protected float getAspectRatio() {
- return mAspectRatio;
- }
-
/**
* Sets both shelf visibility and its height if applicable.
* @return {@code true} if the internal shelf state is changed, {@code false} otherwise.
@@ -165,18 +138,17 @@
* Note that both inset and normal bounds will be calculated here rather than in the caller.
*/
public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
- Rect animatingBounds, DisplayInfo displayInfo) {
+ Rect animatingBounds) {
getInsetBounds(insetBounds);
final Rect defaultBounds = getDefaultBounds(INVALID_SNAP_FRACTION, null);
normalBounds.set(defaultBounds);
if (animatingBounds.isEmpty()) {
animatingBounds.set(defaultBounds);
}
- if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
- transformBoundsToAspectRatio(normalBounds, mAspectRatio,
+ if (isValidPictureInPictureAspectRatio(mPipBoundsState.getAspectRatio())) {
+ transformBoundsToAspectRatio(normalBounds, mPipBoundsState.getAspectRatio(),
false /* useCurrentMinEdgeSize */, false /* useCurrentSize */);
}
- displayInfo.copyFrom(mDisplayInfo);
}
/**
@@ -187,23 +159,6 @@
return mSnapAlgorithm;
}
- public Rect getDisplayBounds() {
- return new Rect(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
- }
-
- public int getDisplayRotation() {
- return mDisplayInfo.rotation;
- }
-
- /**
- * Responds to IPinnedStackListener on {@link DisplayInfo} change.
- * It will normally follow up with a
- * {@link #onMovementBoundsChanged(Rect, Rect, Rect, DisplayInfo)} callback.
- */
- public void onDisplayInfoChanged(DisplayInfo displayInfo) {
- mDisplayInfo.copyFrom(displayInfo);
- }
-
/**
* Responds to IPinnedStackListener on configuration change.
*/
@@ -212,27 +167,16 @@
}
/**
- * Responds to IPinnedStackListener on resetting aspect ratio for the pinned window.
- * It will normally follow up with a
- * {@link #onMovementBoundsChanged(Rect, Rect, Rect, DisplayInfo)} callback.
+ * See {@link #getDestinationBounds(Rect, Size, boolean)}
*/
- public void onAspectRatioChanged(float aspectRatio) {
- mAspectRatio = aspectRatio;
- }
-
- /**
- * See {@link #getDestinationBounds(float, Rect, Size, boolean)}
- */
- public Rect getDestinationBounds(float aspectRatio, Rect bounds, Size minimalSize) {
- return getDestinationBounds(aspectRatio, bounds, minimalSize,
- false /* useCurrentMinEdgeSize */);
+ public Rect getDestinationBounds(Rect bounds, Size minimalSize) {
+ return getDestinationBounds(bounds, minimalSize, false /* useCurrentMinEdgeSize */);
}
/**
* @return {@link Rect} of the destination PiP window bounds.
*/
- public Rect getDestinationBounds(float aspectRatio, Rect bounds,
- Size minimalSize, boolean useCurrentMinEdgeSize) {
+ public Rect getDestinationBounds(Rect bounds, Size minimalSize, boolean useCurrentMinEdgeSize) {
boolean isReentryBounds = false;
final Rect destinationBounds;
if (bounds == null) {
@@ -255,11 +199,10 @@
// Just adjusting bounds (e.g. on aspect ratio changed).
destinationBounds = new Rect(bounds);
}
- if (isValidPictureInPictureAspectRatio(aspectRatio)) {
- transformBoundsToAspectRatio(destinationBounds, aspectRatio, useCurrentMinEdgeSize,
- isReentryBounds);
+ if (isValidPictureInPictureAspectRatio(mPipBoundsState.getAspectRatio())) {
+ transformBoundsToAspectRatio(destinationBounds, mPipBoundsState.getAspectRatio(),
+ useCurrentMinEdgeSize, isReentryBounds);
}
- mAspectRatio = aspectRatio;
return destinationBounds;
}
@@ -267,10 +210,6 @@
return mDefaultAspectRatio;
}
- public void onOverlayChanged(Context context, Display display) {
- mDisplayLayout = new DisplayLayout(context, display);
- }
-
/**
* Updatest the display info and display layout on rotation change. This is needed even when we
* aren't in PIP because the rotation layout is used to calculate the proper insets for the
@@ -279,13 +218,13 @@
public void onDisplayRotationChangedNotInPip(Context context, int toRotation) {
// Update the display layout, note that we have to do this on every rotation even if we
// aren't in PIP since we need to update the display layout to get the right resources
- mDisplayLayout.rotateTo(context.getResources(), toRotation);
+ mPipBoundsState.getDisplayLayout().rotateTo(context.getResources(), toRotation);
// Populate the new {@link #mDisplayInfo}.
// The {@link DisplayInfo} queried from DisplayManager would be the one before rotation,
// therefore, the width/height may require a swap first.
// Moving forward, we should get the new dimensions after rotation from DisplayLayout.
- mDisplayInfo.rotation = toRotation;
+ mPipBoundsState.setDisplayRotation(toRotation);
updateDisplayInfoIfNeeded();
}
@@ -299,7 +238,8 @@
Rect outInsetBounds,
int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) {
// Bail early if the event is not sent to current {@link #mDisplayInfo}
- if ((displayId != mDisplayInfo.displayId) || (fromRotation == toRotation)) {
+ if ((displayId != mPipBoundsState.getDisplayInfo().displayId)
+ || (fromRotation == toRotation)) {
return false;
}
@@ -319,13 +259,13 @@
final float snapFraction = getSnapFraction(postChangeStackBounds);
// Update the display layout
- mDisplayLayout.rotateTo(context.getResources(), toRotation);
+ mPipBoundsState.getDisplayLayout().rotateTo(context.getResources(), toRotation);
// Populate the new {@link #mDisplayInfo}.
// The {@link DisplayInfo} queried from DisplayManager would be the one before rotation,
// therefore, the width/height may require a swap first.
// Moving forward, we should get the new dimensions after rotation from DisplayLayout.
- mDisplayInfo.rotation = toRotation;
+ mPipBoundsState.getDisplayInfo().rotation = toRotation;
updateDisplayInfoIfNeeded();
// Calculate the stack bounds in the new orientation based on same fraction along the
@@ -342,16 +282,17 @@
}
private void updateDisplayInfoIfNeeded() {
+ final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
final boolean updateNeeded;
- if ((mDisplayInfo.rotation == ROTATION_0) || (mDisplayInfo.rotation == ROTATION_180)) {
- updateNeeded = (mDisplayInfo.logicalWidth > mDisplayInfo.logicalHeight);
+ if ((displayInfo.rotation == ROTATION_0) || (displayInfo.rotation == ROTATION_180)) {
+ updateNeeded = (displayInfo.logicalWidth > displayInfo.logicalHeight);
} else {
- updateNeeded = (mDisplayInfo.logicalWidth < mDisplayInfo.logicalHeight);
+ updateNeeded = (displayInfo.logicalWidth < displayInfo.logicalHeight);
}
if (updateNeeded) {
- final int newLogicalHeight = mDisplayInfo.logicalWidth;
- mDisplayInfo.logicalWidth = mDisplayInfo.logicalHeight;
- mDisplayInfo.logicalHeight = newLogicalHeight;
+ final int newLogicalHeight = displayInfo.logicalWidth;
+ displayInfo.logicalWidth = displayInfo.logicalHeight;
+ displayInfo.logicalHeight = newLogicalHeight;
}
}
@@ -368,8 +309,8 @@
* @param stackBounds
*/
public void transformBoundsToAspectRatio(Rect stackBounds) {
- transformBoundsToAspectRatio(stackBounds, mAspectRatio, true /* useCurrentMinEdgeSize */,
- true /* useCurrentSize */);
+ transformBoundsToAspectRatio(stackBounds, mPipBoundsState.getAspectRatio(),
+ true /* useCurrentMinEdgeSize */, true /* useCurrentSize */);
}
/**
@@ -387,8 +328,9 @@
size = mSnapAlgorithm.getSizeForAspectRatio(
new Size(stackBounds.width(), stackBounds.height()), aspectRatio, minEdgeSize);
} else {
+ final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, minEdgeSize,
- mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+ displayInfo.logicalWidth, displayInfo.logicalHeight);
}
final int left = (int) (stackBounds.centerX() - size.getWidth() / 2f);
@@ -438,8 +380,9 @@
} else {
final Rect insetBounds = new Rect();
getInsetBounds(insetBounds);
+ final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
size = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio,
- mDefaultMinSize, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+ mDefaultMinSize, displayInfo.logicalWidth, displayInfo.logicalHeight);
Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds,
0, Math.max(mIsImeShowing ? mImeHeight : 0,
mIsShelfShowing ? mShelfHeight : 0),
@@ -452,11 +395,12 @@
* Populates the bounds on the screen that the PIP can be visible in.
*/
protected void getInsetBounds(Rect outRect) {
- Rect insets = mDisplayLayout.stableInsets();
+ final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
+ Rect insets = mPipBoundsState.getDisplayLayout().stableInsets();
outRect.set(insets.left + mScreenEdgeInsets.x,
insets.top + mScreenEdgeInsets.y,
- mDisplayInfo.logicalWidth - insets.right - mScreenEdgeInsets.x,
- mDisplayInfo.logicalHeight - insets.bottom - mScreenEdgeInsets.y);
+ displayInfo.logicalWidth - insets.right - mScreenEdgeInsets.x,
+ displayInfo.logicalHeight - insets.bottom - mScreenEdgeInsets.y);
}
/**
@@ -510,11 +454,9 @@
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
- pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo);
pw.println(innerPrefix + "mDefaultAspectRatio=" + mDefaultAspectRatio);
pw.println(innerPrefix + "mMinAspectRatio=" + mMinAspectRatio);
pw.println(innerPrefix + "mMaxAspectRatio=" + mMaxAspectRatio);
- pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
pw.println(innerPrefix + "mDefaultStackGravity=" + mDefaultStackGravity);
pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index 2625f16..3a675c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -21,8 +21,10 @@
import android.content.ComponentName;
import android.graphics.Rect;
import android.util.Size;
+import android.view.DisplayInfo;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.common.DisplayLayout;
import java.io.PrintWriter;
import java.util.Objects;
@@ -34,8 +36,11 @@
private static final String TAG = PipBoundsState.class.getSimpleName();
private final @NonNull Rect mBounds = new Rect();
+ private float mAspectRatio;
private PipReentryState mPipReentryState;
private ComponentName mLastPipComponentName;
+ private final DisplayInfo mDisplayInfo = new DisplayInfo();
+ private final DisplayLayout mDisplayLayout = new DisplayLayout();
void setBounds(@NonNull Rect bounds) {
mBounds.set(bounds);
@@ -46,6 +51,14 @@
return new Rect(mBounds);
}
+ public void setAspectRatio(float aspectRatio) {
+ mAspectRatio = aspectRatio;
+ }
+
+ public float getAspectRatio() {
+ return mAspectRatio;
+ }
+
/**
* Save the reentry state to restore to when re-entering PIP mode.
*
@@ -79,6 +92,42 @@
return mLastPipComponentName;
}
+ @NonNull
+ public DisplayInfo getDisplayInfo() {
+ return mDisplayInfo;
+ }
+
+ /**
+ * Update the display info.
+ */
+ public void setDisplayInfo(@NonNull DisplayInfo displayInfo) {
+ mDisplayInfo.copyFrom(displayInfo);
+ }
+
+ public void setDisplayRotation(int rotation) {
+ mDisplayInfo.rotation = rotation;
+ }
+
+ /**
+ * Returns the display's bound.
+ */
+ @NonNull
+ public Rect getDisplayBounds() {
+ return new Rect(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+ }
+
+ /**
+ * Update the display layout.
+ */
+ public void setDisplayLayout(@NonNull DisplayLayout displayLayout) {
+ mDisplayLayout.set(displayLayout);
+ }
+
+ @NonNull
+ public DisplayLayout getDisplayLayout() {
+ return mDisplayLayout;
+ }
+
@VisibleForTesting
void clearReentryState() {
mPipReentryState = null;
@@ -120,6 +169,9 @@
pw.println(prefix + TAG);
pw.println(innerPrefix + "mBounds=" + mBounds);
pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
+ pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
+ pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo);
+ pw.println(innerPrefix + "mDisplayLayout=" + mDisplayLayout);
if (mPipReentryState == null) {
pw.println(innerPrefix + "mPipReentryState=null");
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index cd5d35b..a05aac9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -68,7 +68,6 @@
import com.android.wm.shell.pip.phone.PipMenuActivityController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.pip.phone.PipUpdateThread;
-import com.android.wm.shell.pip.phone.PipUtils;
import com.android.wm.shell.splitscreen.SplitScreen;
import java.io.PrintWriter;
@@ -339,11 +338,11 @@
public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
PictureInPictureParams pictureInPictureParams) {
mShouldIgnoreEnteringPipTransition = true;
- mState = State.ENTERING_PIP;
+ sendOnPipTransitionStarted(componentName, TRANSITION_DIRECTION_TO_PIP);
mPipBoundsState.setLastPipComponentName(componentName);
- return mPipBoundsHandler.getDestinationBounds(
- getAspectRatioOrDefault(pictureInPictureParams),
- null /* bounds */, getMinimalSize(activityInfo));
+ mPipBoundsState.setAspectRatio(getAspectRatioOrDefault(pictureInPictureParams));
+ return mPipBoundsHandler.getDestinationBounds(null /* bounds */,
+ getMinimalSize(activityInfo));
}
/**
@@ -351,7 +350,10 @@
* Expect {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)} afterwards.
*/
public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
- mPipBoundsState.setBounds(destinationBounds);
+ // do nothing if there is no startSwipePipToHome being called before
+ if (mShouldIgnoreEnteringPipTransition) {
+ mPipBoundsState.setBounds(destinationBounds);
+ }
}
/**
@@ -378,7 +380,7 @@
mPipUiEventLoggerLogger.log(
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation()
- != mPipBoundsHandler.getDisplayRotation();
+ != mPipBoundsState.getDisplayInfo().rotation;
final WindowContainerTransaction wct = new WindowContainerTransaction();
final Rect destinationBounds = initialConfig.windowConfiguration.getBounds();
final int direction = syncWithSplitScreenBounds(destinationBounds)
@@ -488,17 +490,15 @@
// If the displayId of the task is different than what PipBoundsHandler has, then update
// it. This is possible if we entered PiP on an external display.
- if (info.displayId != mPipBoundsHandler.getDisplayInfo().displayId
+ if (info.displayId != mPipBoundsState.getDisplayInfo().displayId
&& mOnDisplayIdChangeCallback != null) {
mOnDisplayIdChangeCallback.accept(info.displayId);
}
if (mShouldIgnoreEnteringPipTransition) {
- // Animation has been finished together with Recents, directly apply the sync
- // transaction to PiP here.
- applyEnterPipSyncTransaction(mPipBoundsState.getBounds(), () -> {
- mState = State.ENTERED_PIP;
- });
+ // animation is finished in the Launcher and here we directly apply the final touch.
+ applyEnterPipSyncTransaction(mPipBoundsState.getBounds(),
+ () -> sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP));
mShouldIgnoreEnteringPipTransition = false;
return;
}
@@ -514,9 +514,9 @@
return;
}
- final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- getAspectRatioOrDefault(mPictureInPictureParams),
- null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
+ mPipBoundsState.setAspectRatio(getAspectRatioOrDefault(mPictureInPictureParams));
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(null /* bounds */,
+ getMinimalSize(mTaskInfo.topActivityInfo));
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
@@ -589,11 +589,19 @@
private void sendOnPipTransitionStarted(
@PipAnimationController.TransitionDirection int direction) {
+ sendOnPipTransitionStarted(mTaskInfo.baseActivity, direction);
+ }
+
+ private void sendOnPipTransitionStarted(ComponentName componentName,
+ @PipAnimationController.TransitionDirection int direction) {
+ if (direction == TRANSITION_DIRECTION_TO_PIP) {
+ mState = State.ENTERING_PIP;
+ }
final Rect pipBounds = mPipBoundsState.getBounds();
runOnMainHandler(() -> {
for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
- callback.onPipTransitionStarted(mTaskInfo.baseActivity, direction, pipBounds);
+ callback.onPipTransitionStarted(componentName, direction, pipBounds);
}
});
}
@@ -703,6 +711,7 @@
return;
}
mShouldDeferEnteringPip = false;
+ mShouldIgnoreEnteringPipTransition = false;
mPictureInPictureParams = null;
mState = State.UNDEFINED;
mPipUiEventLoggerLogger.setTaskInfo(null);
@@ -717,8 +726,8 @@
Log.d(TAG, "Ignored onTaskInfoChanged with PiP param: " + newParams);
return;
}
+ // Aspect ratio changed, re-calculate destination bounds.
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- getAspectRatioOrDefault(newParams),
mPipBoundsState.getBounds(), getMinimalSize(info.topActivityInfo),
true /* userCurrentMinEdgeSize */);
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
@@ -735,7 +744,6 @@
public void onFixedRotationFinished(int displayId) {
if (mShouldDeferEnteringPip && mState.isInPip()) {
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- getAspectRatioOrDefault(mPictureInPictureParams),
null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
// schedule a regular animation to ensure all the callbacks are still being sent
enterPipWithAlphaAnimation(destinationBounds, 0 /* durationMs */);
@@ -764,6 +772,10 @@
if (animator == null || !animator.isRunning()
|| animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
if (mState.isInPip() && fromRotation) {
+ // Update bounds state to final destination first. It's important to do this
+ // before finishing & cancelling the transition animation so that the MotionHelper
+ // bounds are synchronized to the destination bounds when the animation ends.
+ mPipBoundsState.setBounds(destinationBoundsOut);
// If we are rotating while there is a current animation, immediately cancel the
// animation (remove the listeners so we don't trigger the normal finish resize
// call that should only happen on the update thread)
@@ -777,7 +789,6 @@
sendOnPipTransitionCancelled(direction);
sendOnPipTransitionFinished(direction);
}
- mPipBoundsState.setBounds(destinationBoundsOut);
// Create a reset surface transaction for the new bounds and update the window
// container transaction
@@ -803,14 +814,13 @@
final Rect currentDestinationBounds = animator.getDestinationBounds();
destinationBoundsOut.set(currentDestinationBounds);
if (!fromImeAdjustment && !fromShelfAdjustment
- && mPipBoundsHandler.getDisplayBounds().contains(currentDestinationBounds)) {
+ && mPipBoundsState.getDisplayBounds().contains(currentDestinationBounds)) {
// no need to update the destination bounds, bail early
return;
}
- final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds(
- getAspectRatioOrDefault(mPictureInPictureParams),
- null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
+ final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds(null /* bounds */,
+ getMinimalSize(mTaskInfo.topActivityInfo));
if (newDestinationBounds.equals(currentDestinationBounds)) return;
if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) {
animator.updateEndValue(newDestinationBounds);
@@ -831,7 +841,7 @@
params.getAspectRatioRational());
mPictureInPictureParams = params;
if (aspectRatioChanged) {
- mPipBoundsHandler.onAspectRatioChanged(params.getAspectRatio());
+ mPipBoundsState.setAspectRatio(params.getAspectRatio());
}
return aspectRatioChanged;
}
@@ -1083,7 +1093,6 @@
return WINDOWING_MODE_UNDEFINED;
}
-
private void animateResizePip(Rect currentBounds, Rect destinationBounds, Rect sourceHintRect,
@PipAnimationController.TransitionDirection int direction, int durationMs) {
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index a089837..823979b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -44,6 +44,7 @@
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipBoundsHandler;
@@ -136,7 +137,7 @@
@Override
public void onDisplayAdded(int displayId) {
- mPipBoundsHandler.setDisplayLayout(
+ mPipBoundsState.setDisplayLayout(
mDisplayController.getDisplayLayout(displayId));
}
};
@@ -184,7 +185,7 @@
@Override
public void onDisplayInfoChanged(DisplayInfo displayInfo) {
- mHandler.post(() -> mPipBoundsHandler.onDisplayInfoChanged(displayInfo));
+ mHandler.post(() -> mPipBoundsState.setDisplayInfo(displayInfo));
}
@Override
@@ -197,8 +198,10 @@
@Override
public void onAspectRatioChanged(float aspectRatio) {
+ // TODO(b/169373982): Remove this callback as it is redundant with PipTaskOrg params
+ // change.
mHandler.post(() -> {
- mPipBoundsHandler.onAspectRatioChanged(aspectRatio);
+ mPipBoundsState.setAspectRatio(aspectRatio);
mTouchHandler.onAspectRatioChanged();
});
}
@@ -231,7 +234,7 @@
mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> {
final DisplayInfo newDisplayInfo = new DisplayInfo();
displayController.getDisplay(displayId).getDisplayInfo(newDisplayInfo);
- mPipBoundsHandler.onDisplayInfoChanged(newDisplayInfo);
+ mPipBoundsState.setDisplayInfo(newDisplayInfo);
updateMovementBounds(null /* toBounds */, false /* fromRotation */,
false /* fromImeAdjustment */, false /* fromShelfAdustment */,
null /* wct */);
@@ -247,7 +250,7 @@
// listener calls back
final DisplayInfo displayInfo = new DisplayInfo();
context.getDisplay().getDisplayInfo(displayInfo);
- mPipBoundsHandler.onDisplayInfoChanged(displayInfo);
+ mPipBoundsState.setDisplayInfo(displayInfo);
try {
mWindowManagerShellWrapper.addPinnedStackListener(
@@ -296,7 +299,7 @@
@Override
public void onOverlayChanged() {
mHandler.post(() -> {
- mPipBoundsHandler.onOverlayChanged(mContext, mContext.getDisplay());
+ mPipBoundsState.setDisplayLayout(new DisplayLayout(mContext, mContext.getDisplay()));
updateMovementBounds(null /* toBounds */,
false /* fromRotation */, false /* fromImeAdjustment */,
false /* fromShelfAdjustment */,
@@ -441,8 +444,9 @@
// Populate inset / normal bounds and DisplayInfo from mPipBoundsHandler before
// passing to mTouchHandler/mPipTaskOrganizer
final Rect outBounds = new Rect(toBounds);
+ mTmpDisplayInfo.copyFrom(mPipBoundsState.getDisplayInfo());
mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
- outBounds, mTmpDisplayInfo);
+ outBounds);
// mTouchHandler would rely on the bounds populated from mPipTaskOrganizer
mPipTaskOrganizer.onMovementBoundsChanged(outBounds, fromRotation, fromImeAdjustment,
fromShelfAdjustment, wct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java
index 22c05fb..64e3758 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.pip.phone;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import android.app.IActivityManager;
@@ -180,25 +181,25 @@
mPauseAction = new RemoteAction(Icon.createWithResource(mContext,
R.drawable.pip_ic_pause_white), pauseDescription, pauseDescription,
PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PAUSE),
- FLAG_UPDATE_CURRENT));
+ FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
String playDescription = mContext.getString(R.string.pip_play);
mPlayAction = new RemoteAction(Icon.createWithResource(mContext,
R.drawable.pip_ic_play_arrow_white), playDescription, playDescription,
PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PLAY),
- FLAG_UPDATE_CURRENT));
+ FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
String nextDescription = mContext.getString(R.string.pip_skip_to_next);
mNextAction = new RemoteAction(Icon.createWithResource(mContext,
R.drawable.pip_ic_skip_next_white), nextDescription, nextDescription,
PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_NEXT),
- FLAG_UPDATE_CURRENT));
+ FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
String prevDescription = mContext.getString(R.string.pip_skip_to_prev);
mPrevAction = new RemoteAction(Icon.createWithResource(mContext,
R.drawable.pip_ic_skip_previous_white), prevDescription, prevDescription,
PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PREV),
- FLAG_UPDATE_CURRENT));
+ FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index a2233e5..e95e4a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -783,8 +783,8 @@
// If User releases the PIP window while it's out of the display bounds, put
// PIP into stashed mode.
if (mEnableStash
- && (animatingBounds.right > mPipBoundsHandler.getDisplayBounds().right
- || animatingBounds.left < mPipBoundsHandler.getDisplayBounds().left)) {
+ && (animatingBounds.right > mPipBoundsState.getDisplayBounds().right
+ || animatingBounds.left < mPipBoundsState.getDisplayBounds().left)) {
mMotionHelper.stashToEdge(vel.x, vel.y, this::flingEndAction /* endAction */);
} else {
mMotionHelper.flingToSnapTarget(vel.x, vel.y,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
index 4f2d4e5..b9422ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
@@ -54,6 +54,7 @@
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipTaskOrganizer;
import java.util.ArrayList;
@@ -106,6 +107,7 @@
private int mSuspendPipResizingReason;
private Context mContext;
+ private PipBoundsState mPipBoundsState;
private PipBoundsHandler mPipBoundsHandler;
private PipTaskOrganizer mPipTaskOrganizer;
private IActivityTaskManager mActivityTaskManager;
@@ -208,9 +210,10 @@
@Override
public void onMovementBoundsChanged(boolean fromImeAdjustment) {
mHandler.post(() -> {
- // Populate the inset / normal bounds and DisplayInfo from mPipBoundsHandler first.
+ mTmpDisplayInfo.copyFrom(mPipBoundsState.getDisplayInfo());
+ // Populate the inset / normal bounds from mPipBoundsHandler first.
mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mPipBounds,
- mDefaultPipBounds, mTmpDisplayInfo);
+ mDefaultPipBounds);
});
}
@@ -226,6 +229,7 @@
}
public PipController(Context context,
+ PipBoundsState pipBoundsState,
PipBoundsHandler pipBoundsHandler,
PipTaskOrganizer pipTaskOrganizer,
WindowManagerShellWrapper windowManagerShellWrapper
@@ -233,13 +237,14 @@
if (!mInitialized) {
mInitialized = true;
mContext = context;
+ mPipBoundsState = pipBoundsState;
mPipNotification = new PipNotification(context, this);
mPipBoundsHandler = pipBoundsHandler;
// Ensure that we have the display info in case we get calls to update the bounds
// before the listener calls back
final DisplayInfo displayInfo = new DisplayInfo();
context.getDisplay().getDisplayInfo(displayInfo);
- mPipBoundsHandler.onDisplayInfoChanged(displayInfo);
+ mPipBoundsState.setDisplayInfo(displayInfo);
mResizeAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
@@ -540,14 +545,14 @@
/**
* Adds a {@link Listener} to PipController.
*/
- public void addListener(Listener listener) {
+ void addListener(Listener listener) {
mListeners.add(listener);
}
/**
* Removes a {@link Listener} from PipController.
*/
- public void removeListener(Listener listener) {
+ void removeListener(Listener listener) {
mListeners.remove(listener);
}
@@ -636,7 +641,7 @@
/**
* Gets the {@link android.media.session.MediaController} for the PIPed activity.
*/
- public MediaController getMediaController() {
+ MediaController getMediaController() {
return mPipMediaController;
}
@@ -650,7 +655,7 @@
* This returns one of {@link #PLAYBACK_STATE_PLAYING}, {@link #PLAYBACK_STATE_PAUSED},
* or {@link #PLAYBACK_STATE_UNAVAILABLE}.
*/
- public int getPlaybackState() {
+ int getPlaybackState() {
if (mPipMediaController == null || mPipMediaController.getPlaybackState() == null) {
return PLAYBACK_STATE_UNAVAILABLE;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index f3dadfc..4f4e7da 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -30,6 +30,8 @@
Consts.TAG_WM_SHELL),
WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
+ WM_SHELL_DRAG_AND_DROP(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM_SHELL),
TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest");
private final boolean mEnabled;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerImeController.java
index ff617ed..eb82357 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerImeController.java
@@ -40,7 +40,7 @@
private static final float ADJUSTED_NONFOCUS_DIM = 0.3f;
- private final SplitScreenTaskOrganizer mSplits;
+ private final SplitScreenTaskListener mSplits;
private final TransactionPool mTransactionPool;
private final Handler mHandler;
private final TaskOrganizer mTaskOrganizer;
@@ -92,7 +92,7 @@
private boolean mPausedTargetAdjusted = false;
private boolean mAdjustedWhileHidden = false;
- DividerImeController(SplitScreenTaskOrganizer splits, TransactionPool pool, Handler handler,
+ DividerImeController(SplitScreenTaskListener splits, TransactionPool pool, Handler handler,
TaskOrganizer taskOrganizer) {
mSplits = splits;
mTransactionPool = pool;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java
index 2b14e8b..c6496ad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java
@@ -155,7 +155,7 @@
private boolean mAdjustedForIme;
private DividerState mState;
- private SplitScreenTaskOrganizer mTiles;
+ private SplitScreenTaskListener mTiles;
boolean mFirstLayout = true;
int mDividerPositionX;
int mDividerPositionY;
@@ -354,7 +354,7 @@
void injectDependencies(SplitScreenController splitScreenController,
DividerWindowManager windowManager, DividerState dividerState,
- DividerCallbacks callback, SplitScreenTaskOrganizer tiles, SplitDisplayLayout sdl,
+ DividerCallbacks callback, SplitScreenTaskListener tiles, SplitDisplayLayout sdl,
DividerImeController imeController, WindowManagerProxy wmProxy) {
mSplitScreenController = splitScreenController;
mWindowManager = windowManager;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDisplayLayout.java
index 3c0f939..7d5e1a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDisplayLayout.java
@@ -47,7 +47,7 @@
private static final int DIVIDER_WIDTH_INACTIVE_DP = 4;
- SplitScreenTaskOrganizer mTiles;
+ SplitScreenTaskListener mTiles;
DisplayLayout mDisplayLayout;
Context mContext;
@@ -62,7 +62,7 @@
Rect mAdjustedPrimary = null;
Rect mAdjustedSecondary = null;
- public SplitDisplayLayout(Context ctx, DisplayLayout dl, SplitScreenTaskOrganizer taskTiles) {
+ public SplitDisplayLayout(Context ctx, DisplayLayout dl, SplitScreenTaskListener taskTiles) {
mTiles = taskTiles;
mDisplayLayout = dl;
mContext = ctx;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 43e4d62..69d428a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -75,7 +75,7 @@
private final DividerState mDividerState = new DividerState();
private final ForcedResizableInfoActivityController mForcedResizableController;
private final Handler mHandler;
- private final SplitScreenTaskOrganizer mSplits;
+ private final SplitScreenTaskListener mSplits;
private final SystemWindows mSystemWindows;
final TransactionPool mTransactionPool;
private final WindowManagerProxy mWindowManagerProxy;
@@ -117,7 +117,7 @@
mTransactionPool = transactionPool;
mWindowManagerProxy = new WindowManagerProxy(syncQueue, shellTaskOrganizer);
mTaskOrganizer = shellTaskOrganizer;
- mSplits = new SplitScreenTaskOrganizer(this, shellTaskOrganizer);
+ mSplits = new SplitScreenTaskListener(this, shellTaskOrganizer);
mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mHandler,
shellTaskOrganizer);
mRotationController =
@@ -164,6 +164,14 @@
// Don't initialize the divider or anything until we get the default display.
}
+ void onSplitScreenSupported() {
+ // Set starting tile bounds based on middle target
+ final WindowContainerTransaction tct = new WindowContainerTransaction();
+ int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
+ mSplitLayout.resizeSplits(midPos, tct);
+ mTaskOrganizer.applyTransaction(tct);
+ }
+
@Override
public boolean isSplitScreenSupported() {
return mSplits.isSplitScreenSupported();
@@ -196,11 +204,6 @@
}
try {
mSplits.init();
- // Set starting tile bounds based on middle target
- final WindowContainerTransaction tct = new WindowContainerTransaction();
- int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
- mSplitLayout.resizeSplits(midPos, tct);
- mTaskOrganizer.applyTransaction(tct);
} catch (Exception e) {
Slog.e(TAG, "Failed to register docked stack listener", e);
removeDivider();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskListener.java
similarity index 71%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskListener.java
index 0a1aadc..191a317 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskListener.java
@@ -23,26 +23,24 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_SPLIT_SCREEN;
-import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
+import static com.android.wm.shell.ShellTaskOrganizer.getWindowingMode;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
import android.app.ActivityManager.RunningTaskInfo;
import android.graphics.Rect;
-import android.os.RemoteException;
import android.util.Log;
-import android.view.Display;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
-import android.window.TaskAppearedInfo;
import androidx.annotation.NonNull;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
import java.io.PrintWriter;
-class SplitScreenTaskOrganizer implements ShellTaskOrganizer.TaskListener {
- private static final String TAG = "SplitScreenTaskOrg";
+class SplitScreenTaskListener implements ShellTaskOrganizer.TaskListener {
+ private static final String TAG = "SplitScreenTaskListener";
private static final boolean DEBUG = SplitScreenController.DEBUG;
private final ShellTaskOrganizer mTaskOrganizer;
@@ -59,25 +57,19 @@
final SurfaceSession mSurfaceSession = new SurfaceSession();
- SplitScreenTaskOrganizer(SplitScreenController splitScreenController,
+ SplitScreenTaskListener(SplitScreenController splitScreenController,
ShellTaskOrganizer shellTaskOrganizer) {
mSplitScreenController = splitScreenController;
mTaskOrganizer = shellTaskOrganizer;
- mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_SPLIT_SCREEN);
}
- void init() throws RemoteException {
+ void init() {
synchronized (this) {
try {
- final TaskAppearedInfo primary = mTaskOrganizer.createRootTask(
- Display.DEFAULT_DISPLAY, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, this);
- final TaskAppearedInfo secondary = mTaskOrganizer.createRootTask(
- Display.DEFAULT_DISPLAY, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, this);
- mPrimary = primary.getTaskInfo();
- mPrimarySurface = primary.getLeash();
- mSecondary = secondary.getTaskInfo();
- mSecondarySurface = secondary.getLeash();
- enableSplitScreenSupportIfNeeded();
+ mTaskOrganizer.createRootTask(
+ DEFAULT_DISPLAY, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, this);
+ mTaskOrganizer.createRootTask(
+ DEFAULT_DISPLAY, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, this);
} catch (Exception e) {
// teardown to prevent callbacks
mTaskOrganizer.removeListener(this);
@@ -98,29 +90,50 @@
mSplitScreenController.mTransactionPool.release(t);
}
- private void enableSplitScreenSupportIfNeeded() {
- if (mSplitScreenSupported || mPrimarySurface == null || mSecondarySurface == null) return;
+ @Override
+ public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
+ synchronized (this) {
+ final int winMode = getWindowingMode(taskInfo);
+ if (winMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ ProtoLog.v(WM_SHELL_TASK_ORG,
+ "%s onTaskAppeared Primary taskId=%d", TAG, taskInfo.taskId);
+ mPrimary = taskInfo;
+ mPrimarySurface = leash;
+ } else if (winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ ProtoLog.v(WM_SHELL_TASK_ORG,
+ "%s onTaskAppeared Secondary taskId=%d", TAG, taskInfo.taskId);
+ mSecondary = taskInfo;
+ mSecondarySurface = leash;
+ } else {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "%s onTaskAppeared unknown taskId=%d winMode=%d",
+ TAG, taskInfo.taskId, winMode);
+ }
- mSplitScreenSupported = true;
+ if (!mSplitScreenSupported && mPrimarySurface != null && mSecondarySurface != null) {
+ mSplitScreenSupported = true;
+ mSplitScreenController.onSplitScreenSupported();
+ ProtoLog.v(WM_SHELL_TASK_ORG, "%s onTaskAppeared Supported", TAG);
- // Initialize dim surfaces:
- mPrimaryDim = new SurfaceControl.Builder(mSurfaceSession)
- .setParent(mPrimarySurface).setColorLayer()
- .setName("Primary Divider Dim")
- .setCallsite("SplitScreenTaskOrganizer.onTaskAppeared")
- .build();
- mSecondaryDim = new SurfaceControl.Builder(mSurfaceSession)
- .setParent(mSecondarySurface).setColorLayer()
- .setName("Secondary Divider Dim")
- .setCallsite("SplitScreenTaskOrganizer.onTaskAppeared")
- .build();
- SurfaceControl.Transaction t = getTransaction();
- t.setLayer(mPrimaryDim, Integer.MAX_VALUE);
- t.setColor(mPrimaryDim, new float[]{0f, 0f, 0f});
- t.setLayer(mSecondaryDim, Integer.MAX_VALUE);
- t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f});
- t.apply();
- releaseTransaction(t);
+ // Initialize dim surfaces:
+ mPrimaryDim = new SurfaceControl.Builder(mSurfaceSession)
+ .setParent(mPrimarySurface).setColorLayer()
+ .setName("Primary Divider Dim")
+ .setCallsite("SplitScreenTaskOrganizer.onTaskAppeared")
+ .build();
+ mSecondaryDim = new SurfaceControl.Builder(mSurfaceSession)
+ .setParent(mSecondarySurface).setColorLayer()
+ .setName("Secondary Divider Dim")
+ .setCallsite("SplitScreenTaskOrganizer.onTaskAppeared")
+ .build();
+ SurfaceControl.Transaction t = getTransaction();
+ t.setLayer(mPrimaryDim, Integer.MAX_VALUE);
+ t.setColor(mPrimaryDim, new float[]{0f, 0f, 0f});
+ t.setLayer(mSecondaryDim, Integer.MAX_VALUE);
+ t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f});
+ t.apply();
+ releaseTransaction(t);
+ }
+ }
}
@Override
@@ -232,10 +245,13 @@
final String innerPrefix = prefix + " ";
final String childPrefix = innerPrefix + " ";
pw.println(prefix + this);
+ pw.println(innerPrefix + "mSplitScreenSupported=" + mSplitScreenSupported);
+ if (mPrimary != null) pw.println(innerPrefix + "mPrimary.taskId=" + mPrimary.taskId);
+ if (mSecondary != null) pw.println(innerPrefix + "mSecondary.taskId=" + mSecondary.taskId);
}
@Override
public String toString() {
- return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_SPLIT_SCREEN);
+ return TAG;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java
index 47e7c99..c51bbeb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java
@@ -88,7 +88,7 @@
mTaskOrganizer = taskOrganizer;
}
- void dismissOrMaximizeDocked(final SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout,
+ void dismissOrMaximizeDocked(final SplitScreenTaskListener tiles, SplitDisplayLayout layout,
final boolean dismissOrMaximize) {
mExecutor.execute(() -> applyDismissSplit(tiles, layout, dismissOrMaximize));
}
@@ -189,7 +189,7 @@
*
* @return whether the home stack is resizable
*/
- boolean applyEnterSplit(SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout) {
+ boolean applyEnterSplit(SplitScreenTaskListener tiles, SplitDisplayLayout layout) {
// Set launchtile first so that any stack created after
// getAllRootTaskInfos and before reparent (even if unlikely) are placed
// correctly.
@@ -242,7 +242,7 @@
* split (thus resulting in the top of the secondary split becoming
* fullscreen. {@code false} resolves the other way.
*/
- void applyDismissSplit(SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout,
+ void applyDismissSplit(SplitScreenTaskListener tiles, SplitDisplayLayout layout,
boolean dismissOrMaximize) {
// Set launch root first so that any task created after getChildContainers and
// before reparent (pretty unlikely) are put into fullscreen.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 4ff2bfc..8c4f546 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -26,7 +26,7 @@
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("statusBarWindowIsAlwaysVisible", enabled, bugId) {
+ all("statusBarWindowIsAlwaysVisible", bugId, enabled) {
this.showsAboveAppWindow(FlickerTestBase.STATUS_BAR_WINDOW_TITLE)
}
}
@@ -36,7 +36,7 @@
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("navBarWindowIsAlwaysVisible", enabled, bugId) {
+ all("navBarWindowIsAlwaysVisible", bugId, enabled) {
this.showsAboveAppWindow(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE)
}
}
@@ -52,7 +52,7 @@
val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
val endingBounds = WindowUtils.getDisplayBounds(endRotation)
if (allStates) {
- all("noUncoveredRegions", enabled, bugId) {
+ all("noUncoveredRegions", bugId, enabled) {
if (startingBounds == endingBounds) {
this.coversAtLeastRegion(startingBounds)
} else {
@@ -76,7 +76,7 @@
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("navBarLayerIsAlwaysVisible", enabled, bugId) {
+ all("navBarLayerIsAlwaysVisible", bugId, enabled) {
this.showsLayer(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE)
}
}
@@ -86,7 +86,7 @@
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("statusBarLayerIsAlwaysVisible", enabled, bugId) {
+ all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
this.showsLayer(FlickerTestBase.STATUS_BAR_WINDOW_TITLE)
}
}
@@ -101,15 +101,15 @@
val startingPos = WindowUtils.getNavigationBarPosition(beginRotation)
val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
- start("navBarLayerRotatesAndScales_StartingPos", enabled, bugId) {
+ start("navBarLayerRotatesAndScales_StartingPos", bugId, enabled) {
this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos)
}
- end("navBarLayerRotatesAndScales_EndingPost", enabled, bugId) {
+ end("navBarLayerRotatesAndScales_EndingPost", bugId, enabled) {
this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, endingPos)
}
if (startingPos == endingPos) {
- all("navBarLayerRotatesAndScales", enabled, bugId) {
+ all("navBarLayerRotatesAndScales", bugId, enabled) {
this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos)
}
}
@@ -125,10 +125,10 @@
val startingPos = WindowUtils.getStatusBarPosition(beginRotation)
val endingPos = WindowUtils.getStatusBarPosition(endRotation)
- start("statusBarLayerRotatesScales_StartingPos", enabled, bugId) {
+ start("statusBarLayerRotatesScales_StartingPos", bugId, enabled) {
this.hasVisibleRegion(FlickerTestBase.STATUS_BAR_WINDOW_TITLE, startingPos)
}
- end("statusBarLayerRotatesScales_EndingPos", enabled, bugId) {
+ end("statusBarLayerRotatesScales_EndingPos", bugId, enabled) {
this.hasVisibleRegion(FlickerTestBase.STATUS_BAR_WINDOW_TITLE, endingPos)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
index 3b66c58..7f8321f 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
@@ -32,6 +32,10 @@
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
+ </intent-filter>
</activity>
<activity android:name=".ImeActivity"
android:taskAffinity="com.android.wm.shell.flicker.testapp.ImeActivity"
@@ -41,6 +45,10 @@
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
+ </intent-filter>
</activity>
</application>
</manifest>
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/LetterboxTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/LetterboxTaskListenerTest.java
new file mode 100644
index 0000000..45d4d5d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/LetterboxTaskListenerTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.wm.shell;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static org.mockito.ArgumentMatchers.eq;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.SurfaceControl;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.TransactionPool;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link LetterboxTaskListener}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LetterboxTaskListenerTest {
+
+ private static final Rect ACTIVITY_BOUNDS = new Rect(300, 200, 700, 400);
+ private static final Rect TASK_BOUNDS = new Rect(200, 100, 800, 500);
+ private static final Rect TASK_BOUNDS_2 = new Rect(300, 200, 800, 500);
+ private static final Point TASK_POSITION_IN_PARENT = new Point(100, 50);
+ private static final Point TASK_POSITION_IN_PARENT_2 = new Point(200, 100);
+
+ private static final Rect EXPECTED_WINDOW_CROP = new Rect(100, 100, 500, 300);
+ private static final Rect EXPECTED_WINDOW_CROP_2 = new Rect(0, 0, 400, 200);
+
+ private static final RunningTaskInfo TASK_INFO = createTaskInfo(
+ /* taskId */ 1, ACTIVITY_BOUNDS, TASK_BOUNDS, TASK_POSITION_IN_PARENT);
+
+ private static final RunningTaskInfo TASK_INFO_2 = createTaskInfo(
+ /* taskId */ 1, ACTIVITY_BOUNDS, TASK_BOUNDS_2, TASK_POSITION_IN_PARENT_2);
+
+ @Mock private SurfaceControl mLeash;
+ @Mock private SurfaceControl.Transaction mTransaction;
+ private LetterboxTaskListener mLetterboxTaskListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mLetterboxTaskListener = new LetterboxTaskListener(
+ new SyncTransactionQueue(
+ new TransactionPool() {
+ @Override
+ public SurfaceControl.Transaction acquire() {
+ return mTransaction;
+ }
+
+ @Override
+ public void release(SurfaceControl.Transaction t) {
+ }
+ },
+ new Handler(Looper.getMainLooper())));
+ }
+
+ @Test
+ public void testOnTaskAppearedAndonTaskInfoChanged_setCorrectPositionAndCrop() {
+ mLetterboxTaskListener.onTaskAppeared(TASK_INFO, mLeash);
+
+ verify(mTransaction).setPosition(
+ eq(mLeash),
+ eq((float) TASK_POSITION_IN_PARENT.x),
+ eq((float) TASK_POSITION_IN_PARENT.y));
+ // Should return activty coordinates offset by task coordinates
+ verify(mTransaction).setWindowCrop(eq(mLeash), eq(EXPECTED_WINDOW_CROP));
+
+ mLetterboxTaskListener.onTaskInfoChanged(TASK_INFO_2);
+
+ verify(mTransaction).setPosition(
+ eq(mLeash),
+ eq((float) TASK_POSITION_IN_PARENT_2.x),
+ eq((float) TASK_POSITION_IN_PARENT_2.y));
+ // Should return activty coordinates offset by task coordinates
+ verify(mTransaction).setWindowCrop(eq(mLeash), eq(EXPECTED_WINDOW_CROP_2));
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testOnTaskAppeared_calledSecondTimeWithSameTaskId_throwsException() {
+ mLetterboxTaskListener.onTaskAppeared(TASK_INFO, mLeash);
+ mLetterboxTaskListener.onTaskAppeared(TASK_INFO, mLeash);
+ }
+
+ private static RunningTaskInfo createTaskInfo(
+ int taskId,
+ final Rect activityBounds,
+ final Rect taskBounds,
+ final Point taskPositionInParent) {
+ RunningTaskInfo taskInfo = new RunningTaskInfo();
+ taskInfo.taskId = taskId;
+ taskInfo.configuration.windowConfiguration.setBounds(taskBounds);
+ taskInfo.letterboxActivityBounds = Rect.copyOrNull(activityBounds);
+ taskInfo.positionInParent = new Point(taskPositionInParent);
+ return taskInfo;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 07a6bda..35a2293c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -16,15 +16,18 @@
package com.android.wm.shell;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_LETTERBOX;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
-
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -34,6 +37,7 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.content.pm.ParceledListSlice;
+import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -42,6 +46,7 @@
import android.window.ITaskOrganizerController;
import android.window.TaskAppearedInfo;
+import androidx.annotation.Nullable;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -242,10 +247,41 @@
assertTrue(gotException);
}
- private RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
+ @Test
+ public void testTaskInfoToTaskListenerType_whenLetterboxBoundsPassed_returnsLetterboxType() {
+ RunningTaskInfo taskInfo = createTaskInfo(
+ /* taskId */ 1,
+ WINDOWING_MODE_FULLSCREEN,
+ /* letterboxActivityBounds */ new Rect(1, 1, 1, 1));
+
+ assertEquals(
+ ShellTaskOrganizer.taskInfoToTaskListenerType(taskInfo),
+ TASK_LISTENER_TYPE_LETTERBOX);
+ }
+
+ @Test
+ public void testTaskInfoToTaskListenerType_whenLetterboxBoundsIsNull_returnsFullscreenType() {
+ RunningTaskInfo taskInfo = createTaskInfo(
+ /* taskId */ 1, WINDOWING_MODE_FULLSCREEN, /* letterboxActivityBounds */ null);
+
+ assertEquals(
+ ShellTaskOrganizer.taskInfoToTaskListenerType(taskInfo),
+ TASK_LISTENER_TYPE_FULLSCREEN);
+ }
+
+ private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
RunningTaskInfo taskInfo = new RunningTaskInfo();
taskInfo.taskId = taskId;
taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
return taskInfo;
}
+
+ private static RunningTaskInfo createTaskInfo(
+ int taskId, int windowingMode, @Nullable Rect letterboxActivityBounds) {
+ RunningTaskInfo taskInfo = new RunningTaskInfo();
+ taskInfo.taskId = taskId;
+ taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
+ taskInfo.letterboxActivityBounds = Rect.copyOrNull(letterboxActivityBounds);
+ return taskInfo;
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
index e0ac8e2..37421d9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
@@ -65,7 +65,7 @@
mPipBoundsState = new PipBoundsState();
mPipBoundsHandler = new PipBoundsHandler(mContext, mPipBoundsState);
- mPipBoundsHandler.onDisplayInfoChanged(mDefaultDisplayInfo);
+ mPipBoundsState.setDisplayInfo(mDefaultDisplayInfo);
}
private void initializeMockResources() {
@@ -123,7 +123,8 @@
(MAX_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2
};
for (float aspectRatio : aspectRatios) {
- final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio,
+ mPipBoundsState.setAspectRatio(aspectRatio);
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
final float actualAspectRatio =
destinationBounds.width() / (destinationBounds.height() * 1f);
@@ -139,7 +140,8 @@
MAX_ASPECT_RATIO * 2
};
for (float aspectRatio : invalidAspectRatios) {
- final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio,
+ mPipBoundsState.setAspectRatio(aspectRatio);
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
final float actualAspectRatio =
destinationBounds.width() / (destinationBounds.height() * 1f);
@@ -155,8 +157,9 @@
final Rect currentBounds = new Rect(0, 0, 0, 100);
currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left;
- final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio,
- currentBounds, EMPTY_MINIMAL_SIZE);
+ mPipBoundsState.setAspectRatio(aspectRatio);
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(currentBounds,
+ EMPTY_MINIMAL_SIZE);
final float actualAspectRatio =
destinationBounds.width() / (destinationBounds.height() * 1f);
@@ -179,7 +182,8 @@
for (int i = 0; i < aspectRatios.length; i++) {
final float aspectRatio = aspectRatios[i];
final Size minimalSize = minimalSizes[i];
- final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio,
+ mPipBoundsState.setAspectRatio(aspectRatio);
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, minimalSize);
assertTrue("Destination bounds is no smaller than minimal requirement",
(destinationBounds.width() == minimalSize.getWidth()
@@ -200,7 +204,8 @@
currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left;
final Size minSize = new Size(currentBounds.width() / 2, currentBounds.height() / 2);
- final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio,
+ mPipBoundsState.setAspectRatio(aspectRatio);
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
currentBounds, minSize);
assertTrue("Destination bounds ignores minimal size",
@@ -210,13 +215,14 @@
@Test
public void getDestinationBounds_reentryStateExists_restoreLastSize() {
- final Rect reentryBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
+ final Rect reentryBounds = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
reentryBounds.scale(1.25f);
final float reentrySnapFraction = mPipBoundsHandler.getSnapFraction(reentryBounds);
mPipBoundsState.saveReentryState(reentryBounds, reentrySnapFraction);
- final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
assertEquals(reentryBounds.width(), destinationBounds.width());
@@ -225,14 +231,15 @@
@Test
public void getDestinationBounds_reentryStateExists_restoreLastPosition() {
- final Rect reentryBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
+ final Rect reentryBounds = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
reentryBounds.offset(0, -100);
final float reentrySnapFraction = mPipBoundsHandler.getSnapFraction(reentryBounds);
mPipBoundsState.saveReentryState(reentryBounds, reentrySnapFraction);
- final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
assertBoundsInclusionWithMargin("restoreLastPosition", reentryBounds, destinationBounds);
@@ -241,11 +248,12 @@
@Test
public void setShelfHeight_offsetBounds() {
final int shelfHeight = 100;
- final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
+ final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
mPipBoundsHandler.setShelfHeight(true, shelfHeight);
- final Rect newPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
oldPosition.offset(0, -shelfHeight);
@@ -255,11 +263,12 @@
@Test
public void onImeVisibilityChanged_offsetBounds() {
final int imeHeight = 100;
- final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
+ final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
mPipBoundsHandler.onImeVisibilityChanged(true, imeHeight);
- final Rect newPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
oldPosition.offset(0, -imeHeight);
@@ -268,12 +277,13 @@
@Test
public void getDestinationBounds_noReentryState_useDefaultBounds() {
- final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
+ final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
mPipBoundsState.clearReentryState();
- final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO,
+ final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
assertBoundsInclusionWithMargin("useDefaultBounds", defaultBounds, actualBounds);
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index e36e355..45795ff 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -133,11 +133,25 @@
///////////////////////////////////////////////////////////////////////////////////////////////
-static jlong RadialGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
- jfloat radius, jlongArray colorArray, jfloatArray posArray, jint tileMode,
+static jlong RadialGradient_create(JNIEnv* env,
+ jobject,
+ jlong matrixPtr,
+ jfloat startX,
+ jfloat startY,
+ jfloat startRadius,
+ jfloat endX,
+ jfloat endY,
+ jfloat endRadius,
+ jlongArray colorArray,
+ jfloatArray posArray,
+ jint tileMode,
jlong colorSpaceHandle) {
- SkPoint center;
- center.set(x, y);
+
+ SkPoint start;
+ start.set(startX, startY);
+
+ SkPoint end;
+ end.set(endX, endY);
std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
@@ -148,11 +162,17 @@
#error Need to convert float array to SkScalar array before calling the following function.
#endif
- sk_sp<SkShader> shader = SkGradientShader::MakeRadial(center, radius, &colors[0],
- GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
- static_cast<SkTileMode>(tileMode), sGradientShaderFlags, nullptr);
+ auto colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
+ auto skTileMode = static_cast<SkTileMode>(tileMode);
+ sk_sp<SkShader> shader = SkGradientShader::MakeTwoPointConical(start, startRadius, end,
+ endRadius, &colors[0], std::move(colorSpace), pos, colors.size(), skTileMode,
+ sGradientShaderFlags, nullptr);
ThrowIAE_IfNull(env, shader);
+ // Explicitly create a new shader with the specified matrix to match existing behavior.
+ // Passing in the matrix in the instantiation above can throw exceptions for non-invertible
+ // matrices. However, makeWithLocalMatrix will still allow for the shader to be created
+ // and skia handles null-shaders internally (i.e. is ignored)
const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
if (matrix) {
shader = shader->makeWithLocalMatrix(*matrix);
@@ -279,7 +299,7 @@
};
static const JNINativeMethod gRadialGradientMethods[] = {
- { "nativeCreate", "(JFFF[J[FIJ)J", (void*)RadialGradient_create },
+ { "nativeCreate", "(JFFFFFF[J[FIJ)J", (void*)RadialGradient_create },
};
static const JNINativeMethod gSweepGradientMethods[] = {
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 42cf53b..3905e0b 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -52,9 +52,11 @@
void registerLocationListener(String provider, in LocationRequest request, in ILocationListener listener, String packageName, String attributionTag, String listenerId);
void unregisterLocationListener(in ILocationListener listener);
- void registerLocationPendingIntent(String provider, in LocationRequest request, in PendingIntent intent, String packageName, String attributionTag);
+ void registerLocationPendingIntent(String provider, in LocationRequest request, in PendingIntent pendingIntent, String packageName, String attributionTag);
void unregisterLocationPendingIntent(in PendingIntent intent);
+ void injectLocation(in Location location);
+
void requestGeofence(in Geofence geofence, in PendingIntent intent, String packageName, String attributionTag);
void removeGeofence(in PendingIntent intent);
@@ -89,7 +91,6 @@
void startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName, String attributionTag);
void flushGnssBatch();
void stopGnssBatch();
- void injectLocation(in Location location);
List<String> getAllProviders();
List<String> getProviders(in Criteria criteria, boolean enabledOnly);
diff --git a/location/java/android/location/LocationListener.java b/location/java/android/location/LocationListener.java
index 2738ff4f..0ff0a72 100644
--- a/location/java/android/location/LocationListener.java
+++ b/location/java/android/location/LocationListener.java
@@ -19,12 +19,11 @@
import android.annotation.NonNull;
import android.os.Bundle;
+import java.util.concurrent.Executor;
+
/**
- * Used for receiving notifications from the LocationManager when
- * the location has changed. These methods are called if the
- * LocationListener has been registered with the location manager service
- * using the {@link LocationManager#requestLocationUpdates(String, long, float, LocationListener)}
- * method.
+ * Used for receiving notifications when the device location has changed. These methods are called
+ * when the listener has been registered with the LocationManager.
*
* <div class="special reference">
* <h3>Developer Guides</h3>
@@ -32,6 +31,8 @@
* <a href="{@docRoot}guide/topics/location/obtaining-user-location.html">Obtaining User
* Location</a> developer guide.</p>
* </div>
+ *
+ * @see LocationManager#requestLocationUpdates(String, LocationRequest, Executor, LocationListener)
*/
public interface LocationListener {
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index ac775ca..3493693 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -216,15 +216,15 @@
* Key used for an extra holding a boolean enabled/disabled status value when a provider
* enabled/disabled event is broadcast using a PendingIntent.
*
- * @see #requestLocationUpdates(String, long, float, PendingIntent)
+ * @see #requestLocationUpdates(String, LocationRequest, PendingIntent)
*/
public static final String KEY_PROVIDER_ENABLED = "providerEnabled";
/**
- * Key used for an extra holding a {@link Location} value when a location change is broadcast
- * using a PendingIntent.
+ * Key used for an extra holding a {@link Location} value when a location change is sent using
+ * a PendingIntent.
*
- * @see #requestLocationUpdates(String, long, float, PendingIntent)
+ * @see #requestLocationUpdates(String, LocationRequest, PendingIntent)
*/
public static final String KEY_LOCATION_CHANGED = "location";
@@ -1322,27 +1322,26 @@
Preconditions.checkArgument(provider != null, "invalid null provider");
Preconditions.checkArgument(locationRequest != null, "invalid null location request");
- synchronized (sLocationListeners) {
- WeakReference<LocationListenerTransport> reference = sLocationListeners.get(listener);
- LocationListenerTransport transport = reference != null ? reference.get() : null;
- if (transport == null) {
- transport = new LocationListenerTransport(listener, executor);
- sLocationListeners.put(listener, new WeakReference<>(transport));
- } else {
- transport.setExecutor(executor);
- }
+ try {
+ synchronized (sLocationListeners) {
+ WeakReference<LocationListenerTransport> reference = sLocationListeners.get(
+ listener);
+ LocationListenerTransport transport = reference != null ? reference.get() : null;
+ if (transport == null) {
+ transport = new LocationListenerTransport(listener, executor);
+ } else {
+ Preconditions.checkState(transport.isRegistered());
+ transport.setExecutor(executor);
+ }
- try {
- // making the service call while under lock is less than ideal since LMS must
- // make sure that callbacks are not made on the same thread - however it is the
- // easiest way to guarantee that clients will not receive callbacks after
- // unregistration is complete.
mService.registerLocationListener(provider, locationRequest, transport,
mContext.getPackageName(), mContext.getAttributionTag(),
AppOpsManager.toReceiverId(listener));
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+
+ sLocationListeners.put(listener, new WeakReference<>(transport));
}
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -1429,23 +1428,17 @@
public void removeUpdates(@NonNull LocationListener listener) {
Preconditions.checkArgument(listener != null, "invalid null listener");
- synchronized (sLocationListeners) {
- WeakReference<LocationListenerTransport> reference = sLocationListeners.remove(
- listener);
- LocationListenerTransport transport = reference != null ? reference.get() : null;
- if (transport != null) {
- transport.unregister();
-
- try {
- // making the service call while under lock is less than ideal since LMS must
- // make sure that callbacks are not made on the same thread - however it is the
- // easiest way to guarantee that clients will not receive callbacks after
- // unregistration is complete.
+ try {
+ synchronized (sLocationListeners) {
+ WeakReference<LocationListenerTransport> ref = sLocationListeners.remove(listener);
+ LocationListenerTransport transport = ref != null ? ref.get() : null;
+ if (transport != null) {
+ transport.unregister();
mService.unregisterLocationListener(transport);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
}
}
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -2568,7 +2561,7 @@
@Nullable private volatile LocationListener mListener;
LocationListenerTransport(LocationListener listener, Executor executor) {
- Preconditions.checkArgument(listener != null, "invalid null listener/callback");
+ Preconditions.checkArgument(listener != null, "invalid null listener");
mListener = listener;
setExecutor(executor);
}
@@ -2578,6 +2571,10 @@
mExecutor = executor;
}
+ boolean isRegistered() {
+ return mListener != null;
+ }
+
void unregister() {
mListener = null;
}
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index 7a3a4b2..32ac374b 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -171,7 +171,9 @@
if (manager != null) {
try {
manager.onSetAllowed(mAllowed);
- } catch (RemoteException | RuntimeException e) {
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (RuntimeException e) {
Log.w(mTag, e);
}
}
@@ -191,7 +193,9 @@
if (manager != null) {
try {
manager.onSetProperties(mProperties);
- } catch (RemoteException | RuntimeException e) {
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (RuntimeException e) {
Log.w(mTag, e);
}
}
@@ -248,7 +252,9 @@
try {
manager.onReportLocation(location);
- } catch (RemoteException | RuntimeException e) {
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (RuntimeException e) {
Log.w(mTag, e);
}
}
@@ -339,6 +345,8 @@
manager.onSetProperties(mProperties);
manager.onSetAllowed(mAllowed);
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (RuntimeException e) {
Log.w(mTag, e);
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 195122d..8b28cc4 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4049,7 +4049,8 @@
// the associated intent will be handled by the component being registered
mediaButtonIntent.setComponent(eventReceiver);
PendingIntent pi = PendingIntent.getBroadcast(getContext(),
- 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
+ 0/*requestCode, ignored*/, mediaButtonIntent,
+ PendingIntent.FLAG_IMMUTABLE);
registerMediaButtonIntent(pi, eventReceiver);
}
@@ -4102,7 +4103,8 @@
// the associated intent will be handled by the component being registered
mediaButtonIntent.setComponent(eventReceiver);
PendingIntent pi = PendingIntent.getBroadcast(getContext(),
- 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
+ 0/*requestCode, ignored*/, mediaButtonIntent,
+ PendingIntent.FLAG_IMMUTABLE);
unregisterMediaButtonIntent(pi);
}
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index 98ca2f9..4b208ce 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -362,28 +362,34 @@
@Override
public void onEvent(int event, int arg, @Nullable ArrayList<Byte> data)
throws RemoteException {
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
+ if (mEventHandler != null) {
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(
EventHandler.MSG_CAS_EVENT, event, arg, data));
+ }
}
@Override
public void onSessionEvent(@NonNull ArrayList<Byte> sessionId,
int event, int arg, @Nullable ArrayList<Byte> data)
throws RemoteException {
- Message msg = mEventHandler.obtainMessage();
- msg.what = EventHandler.MSG_CAS_SESSION_EVENT;
- msg.arg1 = event;
- msg.arg2 = arg;
- Bundle bundle = new Bundle();
- bundle.putByteArray(EventHandler.SESSION_KEY, toBytes(sessionId));
- bundle.putByteArray(EventHandler.DATA_KEY, toBytes(data));
- msg.setData(bundle);
- mEventHandler.sendMessage(msg);
+ if (mEventHandler != null) {
+ Message msg = mEventHandler.obtainMessage();
+ msg.what = EventHandler.MSG_CAS_SESSION_EVENT;
+ msg.arg1 = event;
+ msg.arg2 = arg;
+ Bundle bundle = new Bundle();
+ bundle.putByteArray(EventHandler.SESSION_KEY, toBytes(sessionId));
+ bundle.putByteArray(EventHandler.DATA_KEY, toBytes(data));
+ msg.setData(bundle);
+ mEventHandler.sendMessage(msg);
+ }
}
@Override
public void onStatusUpdate(byte status, int arg)
throws RemoteException {
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
+ if (mEventHandler != null) {
+ mEventHandler.sendMessage(mEventHandler.obtainMessage(
EventHandler.MSG_CAS_STATUS_EVENT, status, arg));
+ }
}
};
diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java
index 0464036..e5163ce 100644
--- a/media/java/android/media/MediaTranscodeManager.java
+++ b/media/java/android/media/MediaTranscodeManager.java
@@ -71,12 +71,12 @@
To transcode a media file, first create a {@link TranscodingRequest} through its builder class
{@link TranscodingRequest.Builder}. Transcode requests are then enqueue to the manager through
{@link MediaTranscodeManager#enqueueRequest(
- TranscodingRequest, Executor,OnTranscodingFinishedListener)}
+ TranscodingRequest, Executor, OnTranscodingFinishedListener)}
TranscodeRequest are processed based on client process's priority and request priority. When a
transcode operation is completed the caller is notified via its
{@link OnTranscodingFinishedListener}.
- In the meantime the caller may use the returned TranscodingJob object to cancel or check the status
- of a specific transcode operation.
+ In the meantime the caller may use the returned TranscodingSession object to cancel or check the
+ status of a specific transcode operation.
<p>
Here is an example where <code>Builder</code> is used to specify all parameters
@@ -145,10 +145,11 @@
*/
public static final int PRIORITY_UNKNOWN = 0;
/**
- * PRIORITY_REALTIME indicates that the transcoding request is time-critical and that the client
- * wants the transcoding result as soon as possible.
+ * PRIORITY_REALTIME indicates that the transcoding request is time-critical and that the
+ * client wants the transcoding result as soon as possible.
* <p> Set PRIORITY_REALTIME only if the transcoding is time-critical as it will involve
- * performance penalty due to resource reallocation to prioritize the jobs with higher priority.
+ * performance penalty due to resource reallocation to prioritize the sessions with higher
+ * priority.
* TODO(hkuang): Add more description of this when priority is finalized.
*/
public static final int PRIORITY_REALTIME = 1;
@@ -156,7 +157,7 @@
/**
* PRIORITY_OFFLINE indicates the transcoding is not time-critical and the client does not need
* the transcoding result as soon as possible.
- * <p>Jobs with PRIORITY_OFFLINE will be scheduled behind PRIORITY_REALTIME. Always set to
+ * <p>Sessions with PRIORITY_OFFLINE will be scheduled behind PRIORITY_REALTIME. Always set to
* PRIORITY_OFFLINE if client does not need the result as soon as possible and could accept
* delay of the transcoding result.
* @hide
@@ -182,12 +183,12 @@
public interface OnTranscodingFinishedListener {
/**
* Called when the transcoding operation has finished. The receiver may use the
- * TranscodingJob to check the result, i.e. whether the operation succeeded, was canceled or
- * if an error occurred.
+ * TranscodingSession to check the result, i.e. whether the operation succeeded, was
+ * canceled or if an error occurred.
*
- * @param transcodingJob The TranscodingJob instance for the finished transcoding operation.
+ * @param session The TranscodingSession instance for the finished transcoding operation.
*/
- void onTranscodingFinished(@NonNull TranscodingJob transcodingJob);
+ void onTranscodingFinished(@NonNull TranscodingSession session);
}
private final Context mContext;
@@ -196,76 +197,80 @@
private final int mPid;
private final int mUid;
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
- private final HashMap<Integer, TranscodingJob> mPendingTranscodingJobs = new HashMap();
+ private final HashMap<Integer, TranscodingSession> mPendingTranscodingSessions = new HashMap();
private final Object mLock = new Object();
@GuardedBy("mLock")
@NonNull private ITranscodingClient mTranscodingClient = null;
private static MediaTranscodeManager sMediaTranscodeManager;
- private void handleTranscodingFinished(int jobId, TranscodingResultParcel result) {
- synchronized (mPendingTranscodingJobs) {
- // Gets the job associated with the jobId and removes it from
- // mPendingTranscodingJobs.
- final TranscodingJob job = mPendingTranscodingJobs.remove(jobId);
+ private void handleTranscodingFinished(int sessionId, TranscodingResultParcel result) {
+ synchronized (mPendingTranscodingSessions) {
+ // Gets the session associated with the sessionId and removes it from
+ // mPendingTranscodingSessions.
+ final TranscodingSession session = mPendingTranscodingSessions.remove(sessionId);
- if (job == null) {
+ if (session == null) {
// This should not happen in reality.
- Log.e(TAG, "Job " + jobId + " is not in PendingJobs");
+ Log.e(TAG, "Session " + sessionId + " is not in Pendingsessions");
return;
}
- // Updates the job status and result.
- job.updateStatusAndResult(TranscodingJob.STATUS_FINISHED,
- TranscodingJob.RESULT_SUCCESS);
+ // Updates the session status and result.
+ session.updateStatusAndResult(TranscodingSession.STATUS_FINISHED,
+ TranscodingSession.RESULT_SUCCESS);
- // Notifies client the job is done.
- if (job.mListener != null && job.mListenerExecutor != null) {
- job.mListenerExecutor.execute(() -> job.mListener.onTranscodingFinished(job));
+ // Notifies client the session is done.
+ if (session.mListener != null && session.mListenerExecutor != null) {
+ session.mListenerExecutor.execute(
+ () -> session.mListener.onTranscodingFinished(session));
}
}
}
- private void handleTranscodingFailed(int jobId, int errorCode) {
- synchronized (mPendingTranscodingJobs) {
- // Gets the job associated with the jobId and removes it from
- // mPendingTranscodingJobs.
- final TranscodingJob job = mPendingTranscodingJobs.remove(jobId);
+ private void handleTranscodingFailed(int sessionId, int errorCode) {
+ synchronized (mPendingTranscodingSessions) {
+ // Gets the session associated with the sessionId and removes it from
+ // mPendingTranscodingSessions.
+ final TranscodingSession session = mPendingTranscodingSessions.remove(sessionId);
- if (job == null) {
+ if (session == null) {
// This should not happen in reality.
- Log.e(TAG, "Job " + jobId + " is not in PendingJobs");
+ Log.e(TAG, "Session " + sessionId + " is not in Pendingsessions");
return;
}
- // Updates the job status and result.
- job.updateStatusAndResult(TranscodingJob.STATUS_FINISHED,
- TranscodingJob.RESULT_ERROR);
+ // Updates the session status and result.
+ session.updateStatusAndResult(TranscodingSession.STATUS_FINISHED,
+ TranscodingSession.RESULT_ERROR);
- // Notifies client the job failed.
- if (job.mListener != null && job.mListenerExecutor != null) {
- job.mListenerExecutor.execute(() -> job.mListener.onTranscodingFinished(job));
+ // Notifies client the session failed.
+ if (session.mListener != null && session.mListenerExecutor != null) {
+ session.mListenerExecutor.execute(
+ () -> session.mListener.onTranscodingFinished(session));
}
}
}
- private void handleTranscodingProgressUpdate(int jobId, int newProgress) {
- synchronized (mPendingTranscodingJobs) {
- // Gets the job associated with the jobId.
- final TranscodingJob job = mPendingTranscodingJobs.get(jobId);
+ private void handleTranscodingProgressUpdate(int sessionId, int newProgress) {
+ synchronized (mPendingTranscodingSessions) {
+ // Gets the session associated with the sessionId.
+ final TranscodingSession session = mPendingTranscodingSessions.get(sessionId);
- if (job == null) {
+ if (session == null) {
// This should not happen in reality.
- Log.e(TAG, "Job " + jobId + " is not in PendingJobs");
+ Log.e(TAG, "Session " + sessionId + " is not in Pendingsessions");
return;
}
- // Updates the job progress.
- job.updateProgress(newProgress);
+ // Updates the session progress.
+ session.updateProgress(newProgress);
// Notifies client the progress update.
- if (job.mProgressUpdateExecutor != null && job.mProgressUpdateListener != null) {
- job.mProgressUpdateExecutor.execute(
- () -> job.mProgressUpdateListener.onProgressUpdate(job, newProgress));
+ if (session.mProgressUpdateExecutor != null
+ && session.mProgressUpdateListener != null) {
+ session.mProgressUpdateExecutor.execute(
+ () -> session.mProgressUpdateListener.onProgressUpdate(session,
+ newProgress));
}
}
}
@@ -294,47 +299,48 @@
/*
* Handle client binder died event.
* Upon receiving a binder died event of the client, we will do the following:
- * 1) For the job that is running, notify the client that the job is failed with error code,
- * so client could choose to retry the job or not.
+ * 1) For the session that is running, notify the client that the session is failed with
+ * error code, so client could choose to retry the session or not.
* TODO(hkuang): Add a new error code to signal service died error.
- * 2) For the jobs that is still pending or paused, we will resubmit the job internally once
- * we successfully reconnect to the service and register a new client.
+ * 2) For the sessions that is still pending or paused, we will resubmit the session
+ * once we successfully reconnect to the service and register a new client.
* 3) When trying to connect to the service and register a new client. The service may need time
* to reboot or never boot up again. So we will retry for a number of times. If we still
- * could not connect, we will notify client job failure for the pending and paused jobs.
+ * could not connect, we will notify client session failure for the pending and paused
+ * sessions.
*/
private void onClientDied() {
synchronized (mLock) {
mTranscodingClient = null;
}
- // Delegates the job notification and retry to the executor as it may take some time.
+ // Delegates the session notification and retry to the executor as it may take some time.
mExecutor.execute(() -> {
- // List to track the jobs that we want to retry.
- List<TranscodingJob> retryJobs = new ArrayList<TranscodingJob>();
+ // List to track the sessions that we want to retry.
+ List<TranscodingSession> retrySessions = new ArrayList<TranscodingSession>();
- // First notify the client of job failure for all the running jobs.
- synchronized (mPendingTranscodingJobs) {
- for (Map.Entry<Integer, TranscodingJob> entry :
- mPendingTranscodingJobs.entrySet()) {
- TranscodingJob job = entry.getValue();
+ // First notify the client of session failure for all the running sessions.
+ synchronized (mPendingTranscodingSessions) {
+ for (Map.Entry<Integer, TranscodingSession> entry :
+ mPendingTranscodingSessions.entrySet()) {
+ TranscodingSession session = entry.getValue();
- if (job.getStatus() == TranscodingJob.STATUS_RUNNING) {
- job.updateStatusAndResult(TranscodingJob.STATUS_FINISHED,
- TranscodingJob.RESULT_ERROR);
+ if (session.getStatus() == TranscodingSession.STATUS_RUNNING) {
+ session.updateStatusAndResult(TranscodingSession.STATUS_FINISHED,
+ TranscodingSession.RESULT_ERROR);
- // Remove the job from pending jobs.
- mPendingTranscodingJobs.remove(entry.getKey());
+ // Remove the session from pending sessions.
+ mPendingTranscodingSessions.remove(entry.getKey());
- if (job.mListener != null && job.mListenerExecutor != null) {
- Log.i(TAG, "Notify client job failed");
- job.mListenerExecutor.execute(
- () -> job.mListener.onTranscodingFinished(job));
+ if (session.mListener != null && session.mListenerExecutor != null) {
+ Log.i(TAG, "Notify client session failed");
+ session.mListenerExecutor.execute(
+ () -> session.mListener.onTranscodingFinished(session));
}
- } else if (job.getStatus() == TranscodingJob.STATUS_PENDING
- || job.getStatus() == TranscodingJob.STATUS_PAUSED) {
- // Add the job to retryJobs to handle them later.
- retryJobs.add(job);
+ } else if (session.getStatus() == TranscodingSession.STATUS_PENDING
+ || session.getStatus() == TranscodingSession.STATUS_PAUSED) {
+ // Add the session to retrySessions to handle them later.
+ retrySessions.add(session);
}
}
}
@@ -351,37 +357,37 @@
}
}
- for (TranscodingJob job : retryJobs) {
- // Notify the job failure if we fails to connect to the service or fail
- // to retry the job.
+ for (TranscodingSession session : retrySessions) {
+ // Notify the session failure if we fails to connect to the service or fail
+ // to retry the session.
if (!haveTranscodingClient) {
// TODO(hkuang): Return correct error code to the client.
- handleTranscodingFailed(job.getJobId(), 0 /*unused */);
+ handleTranscodingFailed(session.getSessionId(), 0 /*unused */);
}
try {
// Do not set hasRetried for retry initiated by MediaTranscodeManager.
- job.retryInternal(false /*setHasRetried*/);
+ session.retryInternal(false /*setHasRetried*/);
} catch (Exception re) {
// TODO(hkuang): Return correct error code to the client.
- handleTranscodingFailed(job.getJobId(), 0 /*unused */);
+ handleTranscodingFailed(session.getSessionId(), 0 /*unused */);
}
}
});
}
- private void updateStatus(int jobId, int status) {
- synchronized (mPendingTranscodingJobs) {
- final TranscodingJob job = mPendingTranscodingJobs.get(jobId);
+ private void updateStatus(int sessionId, int status) {
+ synchronized (mPendingTranscodingSessions) {
+ final TranscodingSession session = mPendingTranscodingSessions.get(sessionId);
- if (job == null) {
+ if (session == null) {
// This should not happen in reality.
- Log.e(TAG, "Job " + jobId + " is not in PendingJobs");
+ Log.e(TAG, "Session " + sessionId + " is not in Pendingsessions");
return;
}
- // Updates the job status.
- job.updateStatus(status);
+ // Updates the session status.
+ session.updateStatus(status);
}
}
@@ -415,40 +421,42 @@
}
@Override
- public void onTranscodingStarted(int jobId) throws RemoteException {
- updateStatus(jobId, TranscodingJob.STATUS_RUNNING);
+ public void onTranscodingStarted(int sessionId) throws RemoteException {
+ updateStatus(sessionId, TranscodingSession.STATUS_RUNNING);
}
@Override
- public void onTranscodingPaused(int jobId) throws RemoteException {
- updateStatus(jobId, TranscodingJob.STATUS_PAUSED);
+ public void onTranscodingPaused(int sessionId) throws RemoteException {
+ updateStatus(sessionId, TranscodingSession.STATUS_PAUSED);
}
@Override
- public void onTranscodingResumed(int jobId) throws RemoteException {
- updateStatus(jobId, TranscodingJob.STATUS_RUNNING);
+ public void onTranscodingResumed(int sessionId) throws RemoteException {
+ updateStatus(sessionId, TranscodingSession.STATUS_RUNNING);
}
@Override
- public void onTranscodingFinished(int jobId, TranscodingResultParcel result)
+ public void onTranscodingFinished(int sessionId, TranscodingResultParcel result)
throws RemoteException {
- handleTranscodingFinished(jobId, result);
+ handleTranscodingFinished(sessionId, result);
}
@Override
- public void onTranscodingFailed(int jobId, int errorCode) throws RemoteException {
- handleTranscodingFailed(jobId, errorCode);
+ public void onTranscodingFailed(int sessionId, int errorCode)
+ throws RemoteException {
+ handleTranscodingFailed(sessionId, errorCode);
}
@Override
- public void onAwaitNumberOfSessionsChanged(int jobId, int oldAwaitNumber,
+ public void onAwaitNumberOfSessionsChanged(int sessionId, int oldAwaitNumber,
int newAwaitNumber) throws RemoteException {
//TODO(hkuang): Implement this.
}
@Override
- public void onProgressUpdate(int jobId, int newProgress) throws RemoteException {
- handleTranscodingProgressUpdate(jobId, newProgress);
+ public void onProgressUpdate(int sessionId, int newProgress)
+ throws RemoteException {
+ handleTranscodingProgressUpdate(sessionId, newProgress);
}
};
@@ -1023,14 +1031,14 @@
* enqueued transcoding operation. The caller can use that instance to query the status or
* progress, and to get the result once the operation has completed.
*/
- public static final class TranscodingJob {
- /** The job is enqueued but not yet running. */
+ public static final class TranscodingSession {
+ /** The session is enqueued but not yet running. */
public static final int STATUS_PENDING = 1;
- /** The job is currently running. */
+ /** The session is currently running. */
public static final int STATUS_RUNNING = 2;
- /** The job is finished. */
+ /** The session is finished. */
public static final int STATUS_FINISHED = 3;
- /** The job is paused. */
+ /** The session is paused. */
public static final int STATUS_PAUSED = 4;
/** @hide */
@@ -1043,13 +1051,13 @@
@Retention(RetentionPolicy.SOURCE)
public @interface Status {}
- /** The job does not have a result yet. */
+ /** The session does not have a result yet. */
public static final int RESULT_NONE = 1;
- /** The job completed successfully. */
+ /** The session completed successfully. */
public static final int RESULT_SUCCESS = 2;
- /** The job encountered an error while running. */
+ /** The session encountered an error while running. */
public static final int RESULT_ERROR = 3;
- /** The job was canceled by the caller. */
+ /** The session was canceled by the caller. */
public static final int RESULT_CANCELED = 4;
/** @hide */
@@ -1067,12 +1075,12 @@
public interface OnProgressUpdateListener {
/**
* Called when the progress changes. The progress is in percentage between 0 and 1,
- * where 0 means that the job has not yet started and 100 means that it has finished.
+ * where 0 means the session has not yet started and 100 means that it has finished.
*
- * @param job The job associated with the progress.
+ * @param session The session associated with the progress.
* @param progress The new progress ranging from 0 ~ 100 inclusive.
*/
- void onProgressUpdate(@NonNull TranscodingJob job,
+ void onProgressUpdate(@NonNull TranscodingSession session,
@IntRange(from = 0, to = 100) int progress);
}
@@ -1096,10 +1104,10 @@
private @Result int mResult = RESULT_NONE;
@GuardedBy("mLock")
private boolean mHasRetried = false;
- // The original request that associated with this job.
+ // The original request that associated with this session.
private final TranscodingRequest mRequest;
- private TranscodingJob(
+ private TranscodingSession(
@NonNull MediaTranscodeManager manager,
@NonNull TranscodingRequest request,
@NonNull TranscodingSessionParcel parcel,
@@ -1147,19 +1155,19 @@
}
}
- private void updateStatusAndResult(@Status int jobStatus,
- @Result int jobResult) {
+ private void updateStatusAndResult(@Status int sessionStatus,
+ @Result int sessionResult) {
synchronized (mLock) {
- mStatus = jobStatus;
- mResult = jobResult;
+ mStatus = sessionStatus;
+ mResult = sessionResult;
}
}
/**
- * Resubmit the transcoding job to the service.
- * Note that only the job that fails or gets cancelled could be retried and each job could
- * be retried only once. After that, Client need to enqueue a new request if they want to
- * try again.
+ * Resubmit the transcoding session to the service.
+ * Note that only the session that fails or gets cancelled could be retried and each session
+ * could be retried only once. After that, Client need to enqueue a new request if they want
+ * to try again.
*
* @throws MediaTranscodingException.ServiceNotAvailableException if the service
* is temporarily unavailable due to internal service rebooting. Client could retry
@@ -1177,11 +1185,11 @@
synchronized (mLock) {
if (mStatus == STATUS_PENDING || mStatus == STATUS_RUNNING) {
throw new UnsupportedOperationException(
- "Failed to retry as job is in processing");
+ "Failed to retry as session is in processing");
}
if (mHasRetried) {
- throw new UnsupportedOperationException("Job has been retried already");
+ throw new UnsupportedOperationException("Session has been retried already");
}
// Get the client interface.
@@ -1191,7 +1199,7 @@
"Service rebooting. Try again later");
}
- synchronized (mManager.mPendingTranscodingJobs) {
+ synchronized (mManager.mPendingTranscodingSessions) {
try {
// Submits the request to MediaTranscoding service.
TranscodingSessionParcel sessionParcel = new TranscodingSessionParcel();
@@ -1200,10 +1208,10 @@
throw new UnsupportedOperationException("Failed to enqueue request");
}
- // Replace the old job id wit the new one.
+ // Replace the old session id wit the new one.
mSessionId = sessionParcel.sessionId;
- // Adds the new job back into pending jobs.
- mManager.mPendingTranscodingJobs.put(mSessionId, this);
+ // Adds the new session back into pending sessions.
+ mManager.mPendingTranscodingSessions.put(mSessionId, this);
} catch (RemoteException re) {
throw new MediaTranscodingException.ServiceNotAvailableException(
"Failed to resubmit request to Transcoding service");
@@ -1215,13 +1223,13 @@
}
/**
- * Cancels the transcoding job and notify the listener.
- * If the job happened to finish before being canceled this call is effectively a no-op and
- * will not update the result in that case.
+ * Cancels the transcoding session and notify the listener.
+ * If the session happened to finish before being canceled this call is effectively a no-op
+ * and will not update the result in that case.
*/
public void cancel() {
synchronized (mLock) {
- // Check if the job is finished already.
+ // Check if the session is finished already.
if (mStatus != STATUS_FINISHED) {
try {
ITranscodingClient client = mManager.getTranscodingClient();
@@ -1230,22 +1238,22 @@
client.cancelSession(mSessionId);
}
} catch (RemoteException re) {
- //TODO(hkuang): Find out what to do if failing to cancel the job.
- Log.e(TAG, "Failed to cancel the job due to exception: " + re);
+ //TODO(hkuang): Find out what to do if failing to cancel the session.
+ Log.e(TAG, "Failed to cancel the session due to exception: " + re);
}
mStatus = STATUS_FINISHED;
mResult = RESULT_CANCELED;
- // Notifies client the job is canceled.
+ // Notifies client the session is canceled.
mListenerExecutor.execute(() -> mListener.onTranscodingFinished(this));
}
}
}
/**
- * Gets the progress of the transcoding job. The progress is between 0 and 100, where 0
- * means that the job has not yet started and 100 means that it is finished. For the
- * cancelled job, the progress will be the last updated progress before it is cancelled.
+ * Gets the progress of the transcoding session. The progress is between 0 and 100, where 0
+ * means that the session has not yet started and 100 means that it is finished. For the
+ * cancelled session, the progress will be the last updated progress before it is cancelled.
* @return The progress.
*/
@IntRange(from = 0, to = 100)
@@ -1256,7 +1264,7 @@
}
/**
- * Gets the status of the transcoding job.
+ * Gets the status of the transcoding session.
* @return The status.
*/
public @Status int getStatus() {
@@ -1266,15 +1274,15 @@
}
/**
- * Gets jobId of the transcoding job.
- * @return job id.
+ * Gets sessionId of the transcoding session.
+ * @return session id.
*/
- public int getJobId() {
+ public int getSessionId() {
return mSessionId;
}
/**
- * Gets the result of the transcoding job.
+ * Gets the result of the transcoding session.
* @return The result.
*/
public @Result int getResult() {
@@ -1323,7 +1331,7 @@
status = String.valueOf(mStatus);
break;
}
- return String.format(" Job: {id: %d, status: %s, result: %s, progress: %d}",
+ return String.format(" session: {id: %d, status: %s, result: %s, progress: %d}",
mSessionId, status, result, mProgress);
}
@@ -1349,13 +1357,13 @@
/**
* Enqueues a TranscodingRequest for execution.
* <p> Upon successfully accepting the request, MediaTranscodeManager will return a
- * {@link TranscodingJob} to the client. Client should use {@link TranscodingJob} to track the
- * progress and get the result.
+ * {@link TranscodingSession} to the client. Client should use {@link TranscodingSession} to
+ * track the progress and get the result.
*
* @param transcodingRequest The TranscodingRequest to enqueue.
* @param listenerExecutor Executor on which the listener is notified.
- * @param listener Listener to get notified when the transcoding job is finished.
- * @return A TranscodingJob for this operation.
+ * @param listener Listener to get notified when the transcoding session is finished.
+ * @return A TranscodingSession for this operation.
* @throws FileNotFoundException if the source Uri or destination Uri could not be opened.
* @throws UnsupportedOperationException if the request could not be fulfilled.
* @throws MediaTranscodingException.ServiceNotAvailableException if the service
@@ -1363,7 +1371,7 @@
* again after receiving this exception.
*/
@NonNull
- public TranscodingJob enqueueRequest(
+ public TranscodingSession enqueueRequest(
@NonNull TranscodingRequest transcodingRequest,
@NonNull @CallbackExecutor Executor listenerExecutor,
@NonNull OnTranscodingFinishedListener listener)
@@ -1382,9 +1390,9 @@
// Submits the request to MediaTranscoding service.
try {
TranscodingSessionParcel sessionParcel = new TranscodingSessionParcel();
- // Synchronizes the access to mPendingTranscodingJobs to make sure the job Id is
- // inserted in the mPendingTranscodingJobs in the callback handler.
- synchronized (mPendingTranscodingJobs) {
+ // Synchronizes the access to mPendingTranscodingSessions to make sure the session Id is
+ // inserted in the mPendingTranscodingSessions in the callback handler.
+ synchronized (mPendingTranscodingSessions) {
synchronized (mLock) {
if (mTranscodingClient == null) {
// Try to register with the service again.
@@ -1402,16 +1410,16 @@
}
}
- // Wraps the TranscodingSessionParcel into a TranscodingJob and returns it to client for
- // tracking.
- TranscodingJob job = new TranscodingJob(this, transcodingRequest,
+ // Wraps the TranscodingSessionParcel into a TranscodingSession and returns it to
+ // client for tracking.
+ TranscodingSession session = new TranscodingSession(this, transcodingRequest,
sessionParcel,
listenerExecutor,
listener);
- // Adds the new job into pending jobs.
- mPendingTranscodingJobs.put(job.getJobId(), job);
- return job;
+ // Adds the new session into pending sessions.
+ mPendingTranscodingSessions.put(session.getSessionId(), session);
+ return session;
}
} catch (RemoteException re) {
throw new UnsupportedOperationException(
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 95199cc..95c4f2a 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -152,7 +152,7 @@
@Override
public final IBinder onBind(Intent intent) {
- return new ITvInputService.Stub() {
+ ITvInputService.Stub tvInputServiceBinder = new ITvInputService.Stub() {
@Override
public void registerCallback(ITvInputServiceCallback cb) {
if (cb != null) {
@@ -181,7 +181,8 @@
args.arg2 = cb;
args.arg3 = inputId;
args.arg4 = sessionId;
- mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args).sendToTarget();
+ mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION,
+ args).sendToTarget();
}
@Override
@@ -228,6 +229,26 @@
deviceInfo).sendToTarget();
}
};
+ IBinder ext = createExtension();
+ if (ext != null) {
+ tvInputServiceBinder.setExtension(ext);
+ }
+ return tvInputServiceBinder;
+ }
+
+ /**
+ * Returns a new {@link android.os.Binder}
+ *
+ * <p> if an extension is provided on top of existing {@link TvInputService}; otherwise,
+ * return {@code null}. Override to provide extended interface.
+ *
+ * @see android.os.Binder#setExtension(IBinder)
+ * @hide
+ */
+ @Nullable
+ @SystemApi
+ public IBinder createExtension() {
+ return null;
}
/**
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerDiedTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerDiedTest.java
index 6b5abcc..7c9842b 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerDiedTest.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerDiedTest.java
@@ -20,8 +20,8 @@
import android.content.Context;
import android.media.MediaFormat;
import android.media.MediaTranscodeManager;
-import android.media.MediaTranscodeManager.TranscodingJob;
import android.media.MediaTranscodeManager.TranscodingRequest;
+import android.media.MediaTranscodeManager.TranscodingSession;
import android.media.MediaTranscodingException;
import android.net.Uri;
import android.os.Bundle;
@@ -125,7 +125,7 @@
}
private MediaTranscodeManager getManager() {
- for (int count = 1; count <= CONNECT_SERVICE_RETRY_COUNT; count++) {
+ for (int count = 1; count <= CONNECT_SERVICE_RETRY_COUNT; count++) {
Log.d(TAG, "Trying to connect to service. Try count: " + count);
MediaTranscodeManager manager = mContext.getSystemService(MediaTranscodeManager.class);
if (manager != null) {
@@ -198,7 +198,7 @@
}
Semaphore transcodeCompleteSemaphore = new Semaphore(0);
- Semaphore jobStartedSemaphore = new Semaphore(0);
+ Semaphore sessionStartedSemaphore = new Semaphore(0);
// Transcode a 15 seconds video, so that the transcoding is not finished when we kill the
// service.
@@ -219,47 +219,52 @@
Log.i(TAG, "transcoding to " + createMediaFormat());
- TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
- transcodingJob -> {
- Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult());
+ TranscodingSession session = mMediaTranscodeManager.enqueueRequest(request,
+ listenerExecutor,
+ TranscodingSession -> {
+ Log.d(TAG,
+ "Transcoding completed with result: " + TranscodingSession.getResult());
transcodeCompleteSemaphore.release();
});
- assertNotNull(job);
+ assertNotNull(session);
AtomicInteger progressUpdateCount = new AtomicInteger(0);
// Set progress update executor and use the same executor as result listener.
- job.setOnProgressUpdateListener(listenerExecutor,
- new TranscodingJob.OnProgressUpdateListener() {
+ session.setOnProgressUpdateListener(listenerExecutor,
+ new TranscodingSession.OnProgressUpdateListener() {
@Override
- public void onProgressUpdate(TranscodingJob job, int newProgress) {
+ public void onProgressUpdate(TranscodingSession session, int newProgress) {
if (newProgress > 0) {
- jobStartedSemaphore.release();
+ sessionStartedSemaphore.release();
}
}
});
- // Wait for progress update so the job is in running state.
- jobStartedSemaphore.tryAcquire(TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
- assertTrue("Job is not running", job.getStatus() == TranscodingJob.STATUS_RUNNING);
+ // Wait for progress update so the session is in running state.
+ sessionStartedSemaphore.tryAcquire(TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ assertTrue("session is not running",
+ session.getStatus() == TranscodingSession.STATUS_RUNNING);
- // Kills the service and expects receiving failure of the job.
+ // Kills the service and expects receiving failure of the session.
executeShellCommand("pkill -f media.transcoding");
Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode result.");
boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
- assertTrue("Invalid job status", job.getStatus() == TranscodingJob.STATUS_FINISHED);
- assertTrue("Invalid job result", job.getResult()== TranscodingJob.RESULT_ERROR);
+ assertTrue("Invalid session status",
+ session.getStatus() == TranscodingSession.STATUS_FINISHED);
+ assertTrue("Invalid session result",
+ session.getResult() == TranscodingSession.RESULT_ERROR);
- boolean retryJob = false;
+ boolean retrysession = false;
// Wait till service is available again.
- Log.d(TAG, "Retry the failed transcoding job");
- while (!retryJob) {
+ Log.d(TAG, "Retry the failed transcoding session");
+ while (!retrysession) {
try {
- job.retry();
- // Break out when job retry succeeds.
+ session.retry();
+ // Break out when session retry succeeds.
break;
} catch (MediaTranscodingException.ServiceNotAvailableException ex) {
// Sleep for 10 milliseconds to wait.
@@ -269,9 +274,11 @@
finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
- // Check the make sure job is successfully finished after retry.
- assertTrue("Invalid job status", job.getStatus() == TranscodingJob.STATUS_FINISHED);
- assertTrue("Invalid job result", job.getResult() == TranscodingJob.RESULT_SUCCESS);
+ // Check the make sure session is successfully finished after retry.
+ assertTrue("Invalid session status",
+ session.getStatus() == TranscodingSession.STATUS_FINISHED);
+ assertTrue("Invalid session result",
+ session.getResult() == TranscodingSession.RESULT_SUCCESS);
}
}
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
index 21ed840..f12ecd5 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
@@ -22,9 +22,9 @@
import android.content.Context;
import android.media.MediaFormat;
import android.media.MediaTranscodeManager;
-import android.media.MediaTranscodeManager.TranscodingJob;
import android.media.MediaTranscodeManager.TranscodingRequest;
import android.media.MediaTranscodeManager.TranscodingRequest.MediaFormatResolver;
+import android.media.MediaTranscodeManager.TranscodingSession;
import android.media.TranscodingTestConfig;
import android.net.Uri;
import android.os.Bundle;
@@ -141,7 +141,7 @@
}
private MediaTranscodeManager getManager() {
- for (int count = 1; count <= CONNECT_SERVICE_RETRY_COUNT; count++) {
+ for (int count = 1; count <= CONNECT_SERVICE_RETRY_COUNT; count++) {
Log.d(TAG, "Trying to connect to service. Try count: " + count);
MediaTranscodeManager manager = mContext.getSystemService(MediaTranscodeManager.class);
if (manager != null) {
@@ -337,23 +337,25 @@
.build();
Executor listenerExecutor = Executors.newSingleThreadExecutor();
- TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
- transcodingJob -> {
- Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult());
+ TranscodingSession session = mMediaTranscodeManager.enqueueRequest(request,
+ listenerExecutor,
+ TranscodingSession -> {
+ Log.d(TAG,
+ "Transcoding completed with result: " + TranscodingSession.getResult());
assertTrue("Transcoding should failed.",
- transcodingJob.getResult() == expectedResult);
+ TranscodingSession.getResult() == expectedResult);
transcodeCompleteSemaphore.release();
});
- assertNotNull(job);
+ assertNotNull(session);
- if (job != null) {
+ if (session != null) {
Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to complete.");
boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertTrue("Transcode failed to complete in time.", finishedOnTime);
}
- if (expectedResult == TranscodingJob.RESULT_SUCCESS) {
+ if (expectedResult == TranscodingSession.RESULT_SUCCESS) {
// Checks the destination file get generated.
File file = new File(dstUri.getPath());
assertTrue("Failed to create destination file", file.exists());
@@ -379,7 +381,8 @@
+ mContext.getPackageName() + "/temp.mp4");
Log.d(TAG, "Transcoding to destination: " + destinationUri);
- testTranscodingWithExpectResult(invalidSrcUri, destinationUri, TranscodingJob.RESULT_ERROR);
+ testTranscodingWithExpectResult(invalidSrcUri, destinationUri,
+ TranscodingSession.RESULT_ERROR);
}
// Tests transcoding to a uri in res folder and expects failure as we could not write to res
@@ -396,7 +399,7 @@
Log.d(TAG, "Transcoding to destination: " + destinationUri);
testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri,
- TranscodingJob.RESULT_ERROR);
+ TranscodingSession.RESULT_ERROR);
}
// Tests transcoding to a uri in internal storage folder and expects success.
@@ -411,7 +414,7 @@
+ mContext.getCacheDir().getAbsolutePath() + "/temp.mp4");
testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri,
- TranscodingJob.RESULT_SUCCESS);
+ TranscodingSession.RESULT_SUCCESS);
}
// Tests transcoding to a uri in internal files directory and expects success.
@@ -425,7 +428,7 @@
Log.i(TAG, "Transcoding to files dir: " + destinationUri);
testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri,
- TranscodingJob.RESULT_SUCCESS);
+ TranscodingSession.RESULT_SUCCESS);
}
// Tests transcoding to a uri in external files directory and expects success.
@@ -438,7 +441,7 @@
Log.i(TAG, "Transcoding to files dir: " + destinationUri);
testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri,
- TranscodingJob.RESULT_SUCCESS);
+ TranscodingSession.RESULT_SUCCESS);
}
@Test
@@ -481,15 +484,17 @@
Log.i(TAG, "transcoding to " + videoTrackFormat);
- TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
- transcodingJob -> {
- Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult());
- assertEquals(transcodingJob.getResult(), TranscodingJob.RESULT_SUCCESS);
+ TranscodingSession session = mMediaTranscodeManager.enqueueRequest(request,
+ listenerExecutor,
+ TranscodingSession -> {
+ Log.d(TAG,
+ "Transcoding completed with result: " + TranscodingSession.getResult());
+ assertEquals(TranscodingSession.getResult(), TranscodingSession.RESULT_SUCCESS);
transcodeCompleteSemaphore.release();
});
- assertNotNull(job);
+ assertNotNull(session);
- if (job != null) {
+ if (session != null) {
Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to cancel.");
boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
@@ -527,18 +532,21 @@
.build();
Executor listenerExecutor = Executors.newSingleThreadExecutor();
- TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
- transcodingJob -> {
- Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult());
- assertEquals(transcodingJob.getResult(), TranscodingJob.RESULT_CANCELED);
+ TranscodingSession session = mMediaTranscodeManager.enqueueRequest(request,
+ listenerExecutor,
+ TranscodingSession -> {
+ Log.d(TAG,
+ "Transcoding completed with result: " + TranscodingSession.getResult());
+ assertEquals(TranscodingSession.getResult(),
+ TranscodingSession.RESULT_CANCELED);
transcodeCompleteSemaphore.release();
});
- assertNotNull(job);
+ assertNotNull(session);
// TODO(hkuang): Wait for progress update before calling cancel to make sure transcoding is
// started.
- job.cancel();
+ session.cancel();
Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to cancel.");
boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
30, TimeUnit.MILLISECONDS);
@@ -571,23 +579,25 @@
Log.i(TAG, "transcoding to " + createMediaFormat());
- TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
- transcodingJob -> {
- Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult());
- assertEquals(transcodingJob.getResult(), TranscodingJob.RESULT_SUCCESS);
+ TranscodingSession session = mMediaTranscodeManager.enqueueRequest(request,
+ listenerExecutor,
+ TranscodingSession -> {
+ Log.d(TAG,
+ "Transcoding completed with result: " + TranscodingSession.getResult());
+ assertEquals(TranscodingSession.getResult(), TranscodingSession.RESULT_SUCCESS);
transcodeCompleteSemaphore.release();
});
- assertNotNull(job);
+ assertNotNull(session);
AtomicInteger progressUpdateCount = new AtomicInteger(0);
// Set progress update executor and use the same executor as result listener.
- job.setOnProgressUpdateListener(listenerExecutor,
- new TranscodingJob.OnProgressUpdateListener() {
+ session.setOnProgressUpdateListener(listenerExecutor,
+ new TranscodingSession.OnProgressUpdateListener() {
int mPreviousProgress = 0;
@Override
- public void onProgressUpdate(TranscodingJob job, int newProgress) {
+ public void onProgressUpdate(TranscodingSession session, int newProgress) {
assertTrue("Invalid proress update", newProgress > mPreviousProgress);
assertTrue("Invalid proress update", newProgress <= 100);
if (newProgress > 0) {
@@ -602,8 +612,9 @@
try {
statusLatch.await();
// The transcoding should not be finished yet as the clip is long.
- assertTrue("Invalid status", job.getStatus() == TranscodingJob.STATUS_RUNNING);
- } catch (InterruptedException e) { }
+ assertTrue("Invalid status", session.getStatus() == TranscodingSession.STATUS_RUNNING);
+ } catch (InterruptedException e) {
+ }
Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to cancel.");
boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
index 10262d9..b152450 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
@@ -20,8 +20,8 @@
import android.content.Context;
import android.media.MediaFormat;
import android.media.MediaTranscodeManager;
-import android.media.MediaTranscodeManager.TranscodingJob;
import android.media.MediaTranscodeManager.TranscodingRequest;
+import android.media.MediaTranscodeManager.TranscodingSession;
import android.media.MediaTranscodingException;
import android.net.Uri;
import android.test.ActivityInstrumentationTestCase2;
@@ -129,17 +129,20 @@
Executor listenerExecutor = Executors.newSingleThreadExecutor();
long startTimeMs = System.currentTimeMillis();
- TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
- transcodingJob -> {
+ TranscodingSession session = mMediaTranscodeManager.enqueueRequest(request,
+ listenerExecutor,
+ TranscodingSession -> {
Log.d(TAG,
- "Transcoding completed with result: " + transcodingJob.getResult());
- assertEquals(transcodingJob.getResult(), TranscodingJob.RESULT_SUCCESS);
+ "Transcoding completed with result: "
+ + TranscodingSession.getResult());
+ assertEquals(TranscodingSession.getResult(),
+ TranscodingSession.RESULT_SUCCESS);
transcodingTime.set(System.currentTimeMillis() - startTimeMs);
totalTimeMs.addAndGet(transcodingTime.get());
transcodeCompleteSemaphore.release();
});
- if (job != null) {
+ if (session != null) {
Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to complete.");
boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
timeoutSeconds, TimeUnit.SECONDS);
@@ -154,7 +157,7 @@
// Calculate the maximum wait time based on minimum transcoding throughput and frame number.
private int calMaxTranscodingWaitTimeSeconds(int numberFrames, int minTranscodingFps) {
- float waitTime = (float) numberFrames / (float) minTranscodingFps;
+ float waitTime = (float) numberFrames / (float) minTranscodingFps;
// If waitTimeSeconds is 0, wait for 1 second at least.
int waitTimeSeconds = (int) Math.ceil(waitTime);
return waitTimeSeconds == 0 ? 1 : waitTimeSeconds;
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 797d3fd..02e1ebe 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -105,6 +105,7 @@
cc_library_shared {
name: "libandroid_net",
defaults: ["libandroid_defaults"],
+ llndk_stubs: "libandroid_net.llndk",
srcs: ["net.c"],
shared_libs: ["libnetd_client"],
@@ -113,7 +114,7 @@
}
llndk_library {
- name: "libandroid_net",
+ name: "libandroid_net.llndk",
export_include_dirs: ["include"],
symbol_file: "libandroid_net.map.txt",
unversioned: true,
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 0a466f4..c503721 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -137,12 +137,24 @@
return nullptr;
}
+ Surface* surface = static_cast<Surface*>(window);
+ sp<IBinder> parentHandle = surface->getSurfaceControlHandle();
+
uint32_t flags = ISurfaceComposerClient::eFXSurfaceBufferState;
- sp<SurfaceControl> surfaceControl =
- client->createWithSurfaceParent(String8(debug_name), 0 /* width */, 0 /* height */,
- // Format is only relevant for buffer queue layers.
- PIXEL_FORMAT_UNKNOWN /* format */, flags,
- static_cast<Surface*>(window));
+ sp<SurfaceControl> surfaceControl;
+ if (parentHandle) {
+ surfaceControl =
+ client->createSurface(String8(debug_name), 0 /* width */, 0 /* height */,
+ // Format is only relevant for buffer queue layers.
+ PIXEL_FORMAT_UNKNOWN /* format */, flags, parentHandle);
+ } else {
+ surfaceControl =
+ client->createWithSurfaceParent(String8(debug_name), 0 /* width */, 0 /* height */,
+ // Format is only relevant for buffer queue layers.
+ PIXEL_FORMAT_UNKNOWN /* format */, flags,
+ static_cast<Surface*>(window));
+ }
+
if (!surfaceControl) {
return nullptr;
}
@@ -164,7 +176,7 @@
client->createSurface(String8(debug_name), 0 /* width */, 0 /* height */,
// Format is only relevant for buffer queue layers.
PIXEL_FORMAT_UNKNOWN /* format */, flags,
- surfaceControlParent);
+ surfaceControlParent->getHandle());
if (!surfaceControl) {
return nullptr;
}
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index addb25d..2d25061 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -97,6 +97,7 @@
field public static final String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
field public static final String MANAGE_EXTERNAL_STORAGE = "android.permission.MANAGE_EXTERNAL_STORAGE";
+ field public static final String MANAGE_ONGOING_CALLS = "android.permission.MANAGE_ONGOING_CALLS";
field public static final String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
field public static final String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
field public static final String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
@@ -10820,7 +10821,6 @@
field public static final String EXTRA_REFERRER = "android.intent.extra.REFERRER";
field public static final String EXTRA_REFERRER_NAME = "android.intent.extra.REFERRER_NAME";
field public static final String EXTRA_REMOTE_INTENT_TOKEN = "android.intent.extra.remote_intent_token";
- field public static final String EXTRA_REMOVED_BY_SYSTEM = "android.intent.extra.REMOVED_BY_SYSTEM";
field public static final String EXTRA_REPLACEMENT_EXTRAS = "android.intent.extra.REPLACEMENT_EXTRAS";
field public static final String EXTRA_REPLACING = "android.intent.extra.REPLACING";
field public static final String EXTRA_RESTRICTIONS_BUNDLE = "android.intent.extra.restrictions_bundle";
@@ -10846,6 +10846,7 @@
field public static final String EXTRA_UID = "android.intent.extra.UID";
field public static final String EXTRA_UNSTARTABLE_REASON = "android.intent.extra.UNSTARTABLE_REASON";
field public static final String EXTRA_USER = "android.intent.extra.USER";
+ field public static final String EXTRA_USER_INITIATED = "android.intent.extra.USER_INITIATED";
field public static final int FILL_IN_ACTION = 1; // 0x1
field public static final int FILL_IN_CATEGORIES = 4; // 0x4
field public static final int FILL_IN_CLIP_DATA = 128; // 0x80
@@ -11718,7 +11719,10 @@
method public android.graphics.drawable.Drawable getIcon(int);
method public CharSequence getLabel();
method public String getName();
+ method public float getProgress();
method public android.os.UserHandle getUser();
+ method public boolean isLoading();
+ method public boolean isStartable();
}
public class LauncherApps {
@@ -11758,6 +11762,7 @@
ctor public LauncherApps.Callback();
method public abstract void onPackageAdded(String, android.os.UserHandle);
method public abstract void onPackageChanged(String, android.os.UserHandle);
+ method public void onPackageProgressChanged(@NonNull String, @NonNull android.os.UserHandle, float);
method public abstract void onPackageRemoved(String, android.os.UserHandle);
method public abstract void onPackagesAvailable(String[], android.os.UserHandle, boolean);
method public void onPackagesSuspended(String[], android.os.UserHandle);
@@ -12265,7 +12270,7 @@
field @Deprecated public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
field public static final int GET_GIDS = 256; // 0x100
field public static final int GET_INSTRUMENTATION = 16; // 0x10
- field public static final int GET_INTENT_FILTERS = 32; // 0x20
+ field @Deprecated public static final int GET_INTENT_FILTERS = 32; // 0x20
field public static final int GET_META_DATA = 128; // 0x80
field public static final int GET_PERMISSIONS = 4096; // 0x1000
field public static final int GET_PROVIDERS = 8; // 0x8
@@ -15435,6 +15440,7 @@
public class RadialGradient extends android.graphics.Shader {
ctor public RadialGradient(float, float, float, @ColorInt @NonNull int[], @Nullable float[], @NonNull android.graphics.Shader.TileMode);
ctor public RadialGradient(float, float, float, @ColorLong @NonNull long[], @Nullable float[], @NonNull android.graphics.Shader.TileMode);
+ ctor public RadialGradient(float, float, @FloatRange(from=0.0f) float, float, float, @FloatRange(from=0.0f, fromInclusive=false) float, @ColorLong @NonNull long[], @Nullable float[], @NonNull android.graphics.Shader.TileMode);
ctor public RadialGradient(float, float, float, @ColorInt int, @ColorInt int, @NonNull android.graphics.Shader.TileMode);
ctor public RadialGradient(float, float, float, @ColorLong long, @ColorLong long, @NonNull android.graphics.Shader.TileMode);
}
@@ -17841,6 +17847,7 @@
public class CaptureResult extends android.hardware.camera2.CameraMetadata<android.hardware.camera2.CaptureResult.Key<?>> {
method @Nullable public <T> T get(android.hardware.camera2.CaptureResult.Key<T>);
+ method @NonNull public String getCameraId();
method public long getFrameNumber();
method @NonNull public java.util.List<android.hardware.camera2.CaptureResult.Key<?>> getKeys();
method @NonNull public android.hardware.camera2.CaptureRequest getRequest();
@@ -35203,6 +35210,11 @@
ctor public OperationCanceledException(String);
}
+ public interface OutcomeReceiver<R, E extends java.lang.Throwable> {
+ method public default void onError(@NonNull E);
+ method public void onResult(@NonNull R);
+ }
+
public final class Parcel {
method public void appendFrom(android.os.Parcel, int, int);
method @Nullable public android.os.IBinder[] createBinderArray();
@@ -37397,6 +37409,9 @@
ctor public CallLog.Calls();
method public static String getLastOutgoingCall(android.content.Context);
field public static final int ANSWERED_EXTERNALLY_TYPE = 7; // 0x7
+ field public static final long AUTO_MISSED_EMERGENCY_CALL = 1L; // 0x1L
+ field public static final long AUTO_MISSED_MAXIMUM_DIALING = 4L; // 0x4L
+ field public static final long AUTO_MISSED_MAXIMUM_RINGING = 2L; // 0x2L
field public static final int BLOCKED_TYPE = 6; // 0x6
field public static final String BLOCK_REASON = "block_reason";
field public static final int BLOCK_REASON_BLOCKED_NUMBER = 3; // 0x3
@@ -37442,6 +37457,8 @@
field public static final String IS_READ = "is_read";
field public static final String LAST_MODIFIED = "last_modified";
field public static final String LIMIT_PARAM_KEY = "limit";
+ field public static final String MISSED_REASON = "missed_reason";
+ field public static final long MISSED_REASON_NOT_MISSED = 0L; // 0x0L
field public static final int MISSED_TYPE = 3; // 0x3
field public static final String NEW = "new";
field public static final String NUMBER = "number";
@@ -37458,6 +37475,13 @@
field public static final int REJECTED_TYPE = 5; // 0x5
field public static final String TRANSCRIPTION = "transcription";
field public static final String TYPE = "type";
+ field public static final long USER_MISSED_CALL_FILTERS_TIMEOUT = 4194304L; // 0x400000L
+ field public static final long USER_MISSED_CALL_SCREENING_SERVICE_SILENCED = 2097152L; // 0x200000L
+ field public static final long USER_MISSED_DND_MODE = 262144L; // 0x40000L
+ field public static final long USER_MISSED_LOW_RING_VOLUME = 524288L; // 0x80000L
+ field public static final long USER_MISSED_NO_ANSWER = 65536L; // 0x10000L
+ field public static final long USER_MISSED_NO_VIBRATE = 1048576L; // 0x100000L
+ field public static final long USER_MISSED_SHORT_RING = 131072L; // 0x20000L
field public static final String VIA_NUMBER = "via_number";
field public static final int VOICEMAIL_TYPE = 4; // 0x4
field public static final String VOICEMAIL_URI = "voicemail_uri";
@@ -41966,6 +41990,7 @@
field public static final int TYPE_RANGE = 2; // 0x2
field public static final int TYPE_STATELESS = 8; // 0x8
field public static final int TYPE_TEMPERATURE = 7; // 0x7
+ field public static final int TYPE_THUMBNAIL = 3; // 0x3
field public static final int TYPE_TOGGLE = 1; // 0x1
field public static final int TYPE_TOGGLE_RANGE = 6; // 0x6
}
@@ -42005,6 +42030,14 @@
field public static final int MODE_UNKNOWN = 0; // 0x0
}
+ public final class ThumbnailTemplate extends android.service.controls.templates.ControlTemplate {
+ ctor public ThumbnailTemplate(@NonNull String, boolean, @NonNull android.graphics.drawable.Icon, @NonNull CharSequence);
+ method @NonNull public CharSequence getContentDescription();
+ method public int getTemplateType();
+ method @NonNull public android.graphics.drawable.Icon getThumbnail();
+ method public boolean isActive();
+ }
+
public final class ToggleRangeTemplate extends android.service.controls.templates.ControlTemplate {
ctor public ToggleRangeTemplate(@NonNull String, @NonNull android.service.controls.templates.ControlButton, @NonNull android.service.controls.templates.RangeTemplate);
ctor public ToggleRangeTemplate(@NonNull String, boolean, @NonNull CharSequence, @NonNull android.service.controls.templates.RangeTemplate);
@@ -44602,6 +44635,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber(android.telecom.PhoneAccountHandle);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String, android.telecom.PhoneAccountHandle);
+ method public boolean hasCompanionInCallServiceAccess();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isInCall();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isInManagedCall();
method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
@@ -45999,6 +46033,7 @@
method @NonNull public java.util.List<java.lang.Integer> getAvailableServices();
method @Nullable public android.telephony.CellIdentity getCellIdentity();
method public int getDomain();
+ method public int getNrState();
method @Nullable public String getRegisteredPlmn();
method public int getTransportType();
method public boolean isRegistered();
@@ -51863,7 +51898,6 @@
}
public interface OnReceiveContentCallback<T extends android.view.View> {
- method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes(@NonNull T);
method public boolean onReceiveContent(@NonNull T, @NonNull android.view.OnReceiveContentCallback.Payload);
}
@@ -52441,7 +52475,7 @@
method @IdRes public int getNextFocusRightId();
method @IdRes public int getNextFocusUpId();
method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
- method @Nullable public android.view.OnReceiveContentCallback<? extends android.view.View> getOnReceiveContentCallback();
+ method @Nullable public String[] getOnReceiveContentMimeTypes();
method @ColorInt public int getOutlineAmbientShadowColor();
method public android.view.ViewOutlineProvider getOutlineProvider();
method @ColorInt public int getOutlineSpotShadowColor();
@@ -52636,6 +52670,7 @@
method public void onProvideContentCaptureStructure(@NonNull android.view.ViewStructure, int);
method public void onProvideStructure(android.view.ViewStructure);
method public void onProvideVirtualStructure(android.view.ViewStructure);
+ method public boolean onReceiveContent(@NonNull android.view.OnReceiveContentCallback.Payload);
method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int);
method @CallSuper protected void onRestoreInstanceState(android.os.Parcelable);
method public void onRtlPropertiesChanged(int);
@@ -52793,7 +52828,7 @@
method public void setOnHoverListener(android.view.View.OnHoverListener);
method public void setOnKeyListener(android.view.View.OnKeyListener);
method public void setOnLongClickListener(@Nullable android.view.View.OnLongClickListener);
- method public void setOnReceiveContentCallback(@Nullable android.view.OnReceiveContentCallback<? extends android.view.View>);
+ method public void setOnReceiveContentCallback(@Nullable String[], @Nullable android.view.OnReceiveContentCallback);
method public void setOnScrollChangeListener(android.view.View.OnScrollChangeListener);
method @Deprecated public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener);
method public void setOnTouchListener(android.view.View.OnTouchListener);
@@ -55536,6 +55571,7 @@
method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
method public android.os.Handler getHandler();
method public CharSequence getSelectedText(int);
+ method @Nullable public default android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
method public CharSequence getTextAfterCursor(int, int);
method public CharSequence getTextBeforeCursor(int, int);
method public boolean performContextMenuAction(int);
@@ -55744,6 +55780,17 @@
method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeNameResId(int);
}
+ public final class SurroundingText implements android.os.Parcelable {
+ ctor public SurroundingText(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0xffffffff) int);
+ method public int describeContents();
+ method @IntRange(from=0xffffffff) public int getOffset();
+ method @IntRange(from=0) public int getSelectionEnd();
+ method @IntRange(from=0) public int getSelectionStart();
+ method @NonNull public CharSequence getText();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.SurroundingText> CREATOR;
+ }
+
}
package android.view.inspector {
@@ -59611,7 +59658,6 @@
method public int getMinWidth();
method public final android.text.method.MovementMethod getMovementMethod();
method public int getOffsetForPosition(float, float);
- method @Nullable public android.view.OnReceiveContentCallback<android.widget.TextView> getOnReceiveContentCallback();
method public android.text.TextPaint getPaint();
method public int getPaintFlags();
method public String getPrivateImeOptions();
@@ -59800,7 +59846,6 @@
public class TextViewOnReceiveContentCallback implements android.view.OnReceiveContentCallback<android.widget.TextView> {
ctor public TextViewOnReceiveContentCallback();
- method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes(@NonNull android.widget.TextView);
method public boolean onReceiveContent(@NonNull android.widget.TextView, @NonNull android.view.OnReceiveContentCallback.Payload);
}
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index e871814..b2d13f9 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -310,6 +310,7 @@
field public static final int config_helpPackageNameKey = 17039387; // 0x104001b
field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
+ field public static final int config_systemAutomotiveProjection = 17039402; // 0x104002a
field public static final int config_systemGallery = 17039399; // 0x1040027
field public static final int config_systemVideoCall = 17039401; // 0x1040029
}
@@ -402,6 +403,7 @@
field public static final String OPSTR_LOADER_USAGE_STATS = "android:loader_usage_stats";
field public static final String OPSTR_MANAGE_EXTERNAL_STORAGE = "android:manage_external_storage";
field public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
+ field public static final String OPSTR_MANAGE_ONGOING_CALLS = "android:manage_ongoing_calls";
field public static final String OPSTR_MUTE_MICROPHONE = "android:mute_microphone";
field public static final String OPSTR_NEIGHBORING_CELLS = "android:neighboring_cells";
field public static final String OPSTR_PLAY_AUDIO = "android:play_audio";
@@ -864,7 +866,6 @@
field public static final int PROVISIONING_TRIGGER_QR_CODE = 2; // 0x2
field public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0; // 0x0
field public static final int STATE_USER_PROFILE_COMPLETE = 4; // 0x4
- field public static final int STATE_USER_PROFILE_FINALIZED = 5; // 0x5
field public static final int STATE_USER_SETUP_COMPLETE = 2; // 0x2
field public static final int STATE_USER_SETUP_FINALIZED = 3; // 0x3
field public static final int STATE_USER_SETUP_INCOMPLETE = 1; // 0x1
@@ -4368,35 +4369,13 @@
}
public final class MediaTranscodeManager {
- method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException, android.media.MediaTranscodingException.ServiceNotAvailableException;
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingSession enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException, android.media.MediaTranscodingException.ServiceNotAvailableException;
field public static final int PRIORITY_REALTIME = 1; // 0x1
field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1
}
@java.lang.FunctionalInterface public static interface MediaTranscodeManager.OnTranscodingFinishedListener {
- method public void onTranscodingFinished(@NonNull android.media.MediaTranscodeManager.TranscodingJob);
- }
-
- public static final class MediaTranscodeManager.TranscodingJob {
- method public void cancel();
- method public int getJobId();
- method @IntRange(from=0, to=100) public int getProgress();
- method public int getResult();
- method public int getStatus();
- method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
- method public void setOnProgressUpdateListener(int, @NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
- field public static final int RESULT_CANCELED = 4; // 0x4
- field public static final int RESULT_ERROR = 3; // 0x3
- field public static final int RESULT_NONE = 1; // 0x1
- field public static final int RESULT_SUCCESS = 2; // 0x2
- field public static final int STATUS_FINISHED = 3; // 0x3
- field public static final int STATUS_PAUSED = 4; // 0x4
- field public static final int STATUS_PENDING = 1; // 0x1
- field public static final int STATUS_RUNNING = 2; // 0x2
- }
-
- @java.lang.FunctionalInterface public static interface MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener {
- method public void onProgressUpdate(@NonNull android.media.MediaTranscodeManager.TranscodingJob, @IntRange(from=0, to=100) int);
+ method public void onTranscodingFinished(@NonNull android.media.MediaTranscodeManager.TranscodingSession);
}
public static final class MediaTranscodeManager.TranscodingRequest {
@@ -4429,6 +4408,28 @@
field public static final String CAPS_SUPPORTS_HEVC = "support-hevc";
}
+ public static final class MediaTranscodeManager.TranscodingSession {
+ method public void cancel();
+ method @IntRange(from=0, to=100) public int getProgress();
+ method public int getResult();
+ method public int getSessionId();
+ method public int getStatus();
+ method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingSession.OnProgressUpdateListener);
+ method public void setOnProgressUpdateListener(int, @NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingSession.OnProgressUpdateListener);
+ field public static final int RESULT_CANCELED = 4; // 0x4
+ field public static final int RESULT_ERROR = 3; // 0x3
+ field public static final int RESULT_NONE = 1; // 0x1
+ field public static final int RESULT_SUCCESS = 2; // 0x2
+ field public static final int STATUS_FINISHED = 3; // 0x3
+ field public static final int STATUS_PAUSED = 4; // 0x4
+ field public static final int STATUS_PENDING = 1; // 0x1
+ field public static final int STATUS_RUNNING = 2; // 0x2
+ }
+
+ @java.lang.FunctionalInterface public static interface MediaTranscodeManager.TranscodingSession.OnProgressUpdateListener {
+ method public void onProgressUpdate(@NonNull android.media.MediaTranscodeManager.TranscodingSession, @IntRange(from=0, to=100) int);
+ }
+
public class PlayerProxy {
method public void pause();
method public void setPan(float);
@@ -4875,6 +4876,7 @@
}
public abstract class TvInputService extends android.app.Service {
+ method @Nullable public android.os.IBinder createExtension();
method @Nullable public android.media.tv.TvInputInfo onHardwareAdded(android.media.tv.TvInputHardwareInfo);
method @Nullable public String onHardwareRemoved(android.media.tv.TvInputHardwareInfo);
method @Nullable public android.media.tv.TvInputInfo onHdmiDeviceAdded(android.hardware.hdmi.HdmiDeviceInfo);
@@ -6633,6 +6635,7 @@
method @NonNull public int[] getTransportTypes();
method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
+ field public static final int NET_CAPABILITY_OEM_PRIVATE = 26; // 0x1a
field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
}
@@ -10657,6 +10660,7 @@
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
+ method public boolean isNrDualConnectivityEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
@@ -10671,6 +10675,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestModemActivityInfo(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.ModemActivityInfo,android.telephony.TelephonyManager.ModemActivityInfoException>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetAllCarrierActions();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetCarrierKeysForImsiEncryption();
@@ -10689,6 +10694,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
+ method public int setNrDualConnectivityState(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean);
@@ -10728,6 +10734,11 @@
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
+ field public static final int ENABLE_NR_DUAL_CONNECTIVITY_INVALID_STATE = 4; // 0x4
+ field public static final int ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED = 1; // 0x1
+ field public static final int ENABLE_NR_DUAL_CONNECTIVITY_RADIO_ERROR = 3; // 0x3
+ field public static final int ENABLE_NR_DUAL_CONNECTIVITY_RADIO_NOT_AVAILABLE = 2; // 0x2
+ field public static final int ENABLE_NR_DUAL_CONNECTIVITY_SUCCESS = 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 public static final String EXTRA_PHONE_IN_ECM_STATE = "android.telephony.extra.PHONE_IN_ECM_STATE";
@@ -10760,6 +10771,9 @@
field public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = 65536L; // 0x10000L
field public static final long NETWORK_TYPE_BITMASK_UMTS = 4L; // 0x4L
field public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; // 0x0L
+ field public static final int NR_DUAL_CONNECTIVITY_DISABLE = 2; // 0x2
+ field public static final int NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE = 3; // 0x3
+ field public static final int NR_DUAL_CONNECTIVITY_ENABLE = 1; // 0x1
field public static final int RADIO_POWER_OFF = 0; // 0x0
field public static final int RADIO_POWER_ON = 1; // 0x1
field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2
@@ -10789,6 +10803,14 @@
field public static final int RESULT_SUCCESS = 0; // 0x0
}
+ public static class TelephonyManager.ModemActivityInfoException extends java.lang.Exception {
+ method public int getErrorCode();
+ field public static final int ERROR_INVALID_INFO_RECEIVED = 2; // 0x2
+ field public static final int ERROR_MODEM_RESPONSE_ERROR = 3; // 0x3
+ field public static final int ERROR_PHONE_NOT_AVAILABLE = 1; // 0x1
+ field public static final int ERROR_UNKNOWN = 0; // 0x0
+ }
+
public final class UiccAccessRule implements android.os.Parcelable {
ctor public UiccAccessRule(byte[], @Nullable String, long);
method public int describeContents();
diff --git a/packages/BackupEncryption/Android.bp b/packages/BackupEncryption/Android.bp
index 68e937c..3a078d2 100644
--- a/packages/BackupEncryption/Android.bp
+++ b/packages/BackupEncryption/Android.bp
@@ -16,6 +16,7 @@
android_app {
name: "BackupEncryption",
+ defaults: ["platform_app_defaults"],
srcs: ["src/**/*.java"],
static_libs: ["backup-encryption-protos", "backuplib"],
optimize: { enabled: false },
diff --git a/packages/BackupRestoreConfirmation/Android.bp b/packages/BackupRestoreConfirmation/Android.bp
index b0222da..6fe039d 100644
--- a/packages/BackupRestoreConfirmation/Android.bp
+++ b/packages/BackupRestoreConfirmation/Android.bp
@@ -16,6 +16,7 @@
android_app {
name: "BackupRestoreConfirmation",
+ defaults: ["platform_app_defaults"],
srcs: ["src/**/*.java"],
platform_apis: true,
certificate: "platform",
diff --git a/packages/CarSystemUI/res/values-af/strings.xml b/packages/CarSystemUI/res/values-af/strings.xml
index b377ec4..cf288d7 100644
--- a/packages/CarSystemUI/res/values-af/strings.xml
+++ b/packages/CarSystemUI/res/values-af/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Enige gebruiker kan programme vir al die ander gebruikers opdateer."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Laai tans"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Laai tans gebruiker (van <xliff:g id="FROM_USER">%1$d</xliff:g> na <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Maak toe"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-am/strings.xml b/packages/CarSystemUI/res/values-am/strings.xml
index 4f2bba8..8281631 100644
--- a/packages/CarSystemUI/res/values-am/strings.xml
+++ b/packages/CarSystemUI/res/values-am/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"ማንኛውም ተጠቃሚ መተግበሪያዎችን ለሌሎች ተጠቃሚዎች ሁሉ ማዘመን ይችላል።"</string>
<string name="car_loading_profile" msgid="4507385037552574474">"በመጫን ላይ"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ተጠቃሚን (ከ<xliff:g id="FROM_USER">%1$d</xliff:g> ወደ <xliff:g id="TO_USER">%2$d</xliff:g>) በመጫን ላይ"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"ዝጋ"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-as/strings.xml b/packages/CarSystemUI/res/values-as/strings.xml
index edc3621..d871055 100644
--- a/packages/CarSystemUI/res/values-as/strings.xml
+++ b/packages/CarSystemUI/res/values-as/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"যিকোনো ব্যৱহাৰকাৰীয়ে অন্য ব্যৱহাৰকাৰীৰ বাবে এপ্সমূহ আপডে’ট কৰিব পাৰে।"</string>
<string name="car_loading_profile" msgid="4507385037552574474">"ল’ড হৈ আছে"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ব্যৱহাৰকাৰী ল’ড হৈ আছে (<xliff:g id="FROM_USER">%1$d</xliff:g>ৰ পৰা to <xliff:g id="TO_USER">%2$d</xliff:g>লৈ)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"বন্ধ কৰক"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-az/strings.xml b/packages/CarSystemUI/res/values-az/strings.xml
index 398f5c3..89c9eb4 100644
--- a/packages/CarSystemUI/res/values-az/strings.xml
+++ b/packages/CarSystemUI/res/values-az/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"İstənilən istifadəçi digər bütün istifadəçilər üçün tətbiqləri güncəlləyə bilər."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Yüklənir"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"İstifadəçi yüklənir (<xliff:g id="FROM_USER">%1$d</xliff:g>-<xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Qapadın"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-b+sr+Latn/strings.xml b/packages/CarSystemUI/res/values-b+sr+Latn/strings.xml
index 6c1979f..6aee013 100644
--- a/packages/CarSystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/CarSystemUI/res/values-b+sr+Latn/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Svaki korisnik može da ažurira aplikacije za sve ostale korisnike."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Učitava se"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Profil korisnika se učitava (iz<xliff:g id="FROM_USER">%1$d</xliff:g> u <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Zatvori"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-be/strings.xml b/packages/CarSystemUI/res/values-be/strings.xml
index 4e97948..fde4273 100644
--- a/packages/CarSystemUI/res/values-be/strings.xml
+++ b/packages/CarSystemUI/res/values-be/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Кожны карыстальнік прылады можа абнаўляць праграмы для ўсіх іншых карыстальнікаў."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Ідзе загрузка"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Ідзе загрузка профілю карыстальніка (ад <xliff:g id="FROM_USER">%1$d</xliff:g> да <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Закрыць"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-bg/strings.xml b/packages/CarSystemUI/res/values-bg/strings.xml
index 7dfab54..25f2845 100644
--- a/packages/CarSystemUI/res/values-bg/strings.xml
+++ b/packages/CarSystemUI/res/values-bg/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Всеки потребител може да актуализира приложенията за всички останали потребители."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Зарежда се"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Потребителят се зарежда (от <xliff:g id="FROM_USER">%1$d</xliff:g> до <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Затваряне"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-bs/strings.xml b/packages/CarSystemUI/res/values-bs/strings.xml
index 119f2d7b..588771e 100644
--- a/packages/CarSystemUI/res/values-bs/strings.xml
+++ b/packages/CarSystemUI/res/values-bs/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Bilo koji korisnik može ažurirati aplikacije za sve druge korisnike."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Učitavanje"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Učitavanje korisnika (od korisnika <xliff:g id="FROM_USER">%1$d</xliff:g> do korisnika <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Zatvori"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-ca/strings.xml b/packages/CarSystemUI/res/values-ca/strings.xml
index b1e722e..cbd469b 100644
--- a/packages/CarSystemUI/res/values-ca/strings.xml
+++ b/packages/CarSystemUI/res/values-ca/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Qualsevol usuari pot actualitzar les aplicacions de la resta d\'usuaris."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"S\'està carregant"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"S\'està carregant l\'usuari (de <xliff:g id="FROM_USER">%1$d</xliff:g> a <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Tanca"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-cs/strings.xml b/packages/CarSystemUI/res/values-cs/strings.xml
index dd4472f..7657e32 100644
--- a/packages/CarSystemUI/res/values-cs/strings.xml
+++ b/packages/CarSystemUI/res/values-cs/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Každý uživatel může aktualizovat aplikace všech ostatních uživatelů."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Načítání"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Načítání uživatele (předchozí: <xliff:g id="FROM_USER">%1$d</xliff:g>, následující: <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Zavřít"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-da/strings.xml b/packages/CarSystemUI/res/values-da/strings.xml
index 6c08aa5..120929e 100644
--- a/packages/CarSystemUI/res/values-da/strings.xml
+++ b/packages/CarSystemUI/res/values-da/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Alle brugere kan opdatere apps for alle andre brugere."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Indlæser"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Indlæser bruger (fra <xliff:g id="FROM_USER">%1$d</xliff:g> til <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Luk"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-el/strings.xml b/packages/CarSystemUI/res/values-el/strings.xml
index 66f8d18..9b24fa4 100644
--- a/packages/CarSystemUI/res/values-el/strings.xml
+++ b/packages/CarSystemUI/res/values-el/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Οποιοσδήποτε χρήστης μπορεί να ενημερώσει τις εφαρμογές για όλους τους άλλους χρήστες."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Φόρτωση"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Φόρτωση χρήστη (από <xliff:g id="FROM_USER">%1$d</xliff:g> έως <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Κλείσιμο"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-en-rAU/strings.xml b/packages/CarSystemUI/res/values-en-rAU/strings.xml
index b3e358f..8eb76c2 100644
--- a/packages/CarSystemUI/res/values-en-rAU/strings.xml
+++ b/packages/CarSystemUI/res/values-en-rAU/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Any user can update apps for all other users."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Loading"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Loading user (from <xliff:g id="FROM_USER">%1$d</xliff:g> to <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Close"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-en-rCA/strings.xml b/packages/CarSystemUI/res/values-en-rCA/strings.xml
index b3e358f..8eb76c2 100644
--- a/packages/CarSystemUI/res/values-en-rCA/strings.xml
+++ b/packages/CarSystemUI/res/values-en-rCA/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Any user can update apps for all other users."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Loading"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Loading user (from <xliff:g id="FROM_USER">%1$d</xliff:g> to <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Close"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-en-rGB/strings.xml b/packages/CarSystemUI/res/values-en-rGB/strings.xml
index b3e358f..8eb76c2 100644
--- a/packages/CarSystemUI/res/values-en-rGB/strings.xml
+++ b/packages/CarSystemUI/res/values-en-rGB/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Any user can update apps for all other users."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Loading"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Loading user (from <xliff:g id="FROM_USER">%1$d</xliff:g> to <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Close"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-en-rIN/strings.xml b/packages/CarSystemUI/res/values-en-rIN/strings.xml
index b3e358f..8eb76c2 100644
--- a/packages/CarSystemUI/res/values-en-rIN/strings.xml
+++ b/packages/CarSystemUI/res/values-en-rIN/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Any user can update apps for all other users."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Loading"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Loading user (from <xliff:g id="FROM_USER">%1$d</xliff:g> to <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Close"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-en-rXC/strings.xml b/packages/CarSystemUI/res/values-en-rXC/strings.xml
index eaf6f51..37a568b 100644
--- a/packages/CarSystemUI/res/values-en-rXC/strings.xml
+++ b/packages/CarSystemUI/res/values-en-rXC/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Any user can update apps for all other users."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Loading"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Loading user (from <xliff:g id="FROM_USER">%1$d</xliff:g> to <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Close"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-es-rUS/strings.xml b/packages/CarSystemUI/res/values-es-rUS/strings.xml
index 6a5f8ce..16aba86 100644
--- a/packages/CarSystemUI/res/values-es-rUS/strings.xml
+++ b/packages/CarSystemUI/res/values-es-rUS/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Cualquier usuario podrá actualizar las apps de otras personas."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Cargando"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Cargando usuario (de <xliff:g id="FROM_USER">%1$d</xliff:g> a <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Cerrar"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-es/strings.xml b/packages/CarSystemUI/res/values-es/strings.xml
index c43d7e5..8aad2ca 100644
--- a/packages/CarSystemUI/res/values-es/strings.xml
+++ b/packages/CarSystemUI/res/values-es/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Cualquier usuario puede actualizar las aplicaciones del resto de los usuarios."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Cargando"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Cargando usuario (de <xliff:g id="FROM_USER">%1$d</xliff:g> a <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Cerrar"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-et/strings.xml b/packages/CarSystemUI/res/values-et/strings.xml
index ad82d5f..14ec9df 100644
--- a/packages/CarSystemUI/res/values-et/strings.xml
+++ b/packages/CarSystemUI/res/values-et/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Iga kasutaja saab rakendusi värskendada kõigi teiste kasutajate jaoks."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Laadimine"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Kasutaja laadimine (<xliff:g id="FROM_USER">%1$d</xliff:g> > <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Sule"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-fa/strings.xml b/packages/CarSystemUI/res/values-fa/strings.xml
index ef37b65..3f53b11 100644
--- a/packages/CarSystemUI/res/values-fa/strings.xml
+++ b/packages/CarSystemUI/res/values-fa/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"هر کاربری میتواند برنامهها را برای همه کاربران دیگر بهروزرسانی کند."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"درحال بارگیری"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"بارگیری کاربر (از <xliff:g id="FROM_USER">%1$d</xliff:g> تا <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"بستن"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-fi/strings.xml b/packages/CarSystemUI/res/values-fi/strings.xml
index 10bb0c5..79b53f6 100644
--- a/packages/CarSystemUI/res/values-fi/strings.xml
+++ b/packages/CarSystemUI/res/values-fi/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Kaikki käyttäjät voivat päivittää muiden käyttäjien sovelluksia."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Ladataan"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Ladataan käyttäjäprofiilia (<xliff:g id="FROM_USER">%1$d</xliff:g>–<xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Sulje"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-fr-rCA/strings.xml b/packages/CarSystemUI/res/values-fr-rCA/strings.xml
index bebd3f4..b190549 100644
--- a/packages/CarSystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/CarSystemUI/res/values-fr-rCA/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Tout utilisateur peut mettre à jour les applications pour tous les autres utilisateurs."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Chargement en cours…"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Chargement de l\'utilisateur (de <xliff:g id="FROM_USER">%1$d</xliff:g> vers <xliff:g id="TO_USER">%2$d</xliff:g>) en cours…"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Fermer"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-fr/strings.xml b/packages/CarSystemUI/res/values-fr/strings.xml
index 3d498d2..5a905a0 100644
--- a/packages/CarSystemUI/res/values-fr/strings.xml
+++ b/packages/CarSystemUI/res/values-fr/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"N\'importe quel utilisateur peut mettre à jour les applications pour tous les autres utilisateurs."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Chargement…"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Chargement de l\'utilisateur (de <xliff:g id="FROM_USER">%1$d</xliff:g> à <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Fermer"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-hi/strings.xml b/packages/CarSystemUI/res/values-hi/strings.xml
index 95454a5..83321fd 100644
--- a/packages/CarSystemUI/res/values-hi/strings.xml
+++ b/packages/CarSystemUI/res/values-hi/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"कोई भी उपयोगकर्ता, बाकी सभी उपयोगकर्ताओं के लिए ऐप्लिकेशन अपडेट कर सकता है."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"लोड हो रही है"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"उपयोगकर्ता को लोड किया जा रहा है (<xliff:g id="FROM_USER">%1$d</xliff:g> से <xliff:g id="TO_USER">%2$d</xliff:g> पर)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"बंद करें"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-hr/strings.xml b/packages/CarSystemUI/res/values-hr/strings.xml
index f3aaf63..872fc69 100644
--- a/packages/CarSystemUI/res/values-hr/strings.xml
+++ b/packages/CarSystemUI/res/values-hr/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Svaki korisnik može ažurirati aplikacije za ostale korisnike."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Učitavanje"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Učitavanje korisnika (od <xliff:g id="FROM_USER">%1$d</xliff:g> do <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Zatvori"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-hu/strings.xml b/packages/CarSystemUI/res/values-hu/strings.xml
index b63ba8b..63328f3 100644
--- a/packages/CarSystemUI/res/values-hu/strings.xml
+++ b/packages/CarSystemUI/res/values-hu/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Bármely felhasználó frissítheti az alkalmazásokat az összes felhasználó számára."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Betöltés"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Felhasználó betöltése (<xliff:g id="FROM_USER">%1$d</xliff:g> → <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Bezárás"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-hy/strings.xml b/packages/CarSystemUI/res/values-hy/strings.xml
index e2a2c6b..778f695 100644
--- a/packages/CarSystemUI/res/values-hy/strings.xml
+++ b/packages/CarSystemUI/res/values-hy/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Ցանկացած օգտատեր կարող է թարմացնել հավելվածները բոլոր մյուս հաշիվների համար։"</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Բեռնում"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Օգտատերը բեռնվում է (<xliff:g id="FROM_USER">%1$d</xliff:g> – <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Փակել"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-in/strings.xml b/packages/CarSystemUI/res/values-in/strings.xml
index 0a70d26..386d79e 100644
--- a/packages/CarSystemUI/res/values-in/strings.xml
+++ b/packages/CarSystemUI/res/values-in/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Setiap pengguna dapat mengupdate aplikasi untuk semua pengguna lain."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Memuat"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Memuat pengguna (dari <xliff:g id="FROM_USER">%1$d</xliff:g> menjadi <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Tutup"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-is/strings.xml b/packages/CarSystemUI/res/values-is/strings.xml
index ea6b031..5a927aa0 100644
--- a/packages/CarSystemUI/res/values-is/strings.xml
+++ b/packages/CarSystemUI/res/values-is/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Allir notendur geta uppfært forrit fyrir alla aðra notendur."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Hleður"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Hleður notanda (frá <xliff:g id="FROM_USER">%1$d</xliff:g> til <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Loka"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-it/strings.xml b/packages/CarSystemUI/res/values-it/strings.xml
index ecbcd5d..41d7ad4 100644
--- a/packages/CarSystemUI/res/values-it/strings.xml
+++ b/packages/CarSystemUI/res/values-it/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Qualsiasi utente può aggiornare le app per tutti gli altri."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Caricamento"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Caricamento dell\'utente (da <xliff:g id="FROM_USER">%1$d</xliff:g> a <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Chiudi"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-iw/strings.xml b/packages/CarSystemUI/res/values-iw/strings.xml
index fe182a3..f419cac 100644
--- a/packages/CarSystemUI/res/values-iw/strings.xml
+++ b/packages/CarSystemUI/res/values-iw/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"כל משתמש יכול לעדכן אפליקציות לכל שאר המשתמשים."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"בטעינה"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"המשתמש בטעינה (מהמשתמש <xliff:g id="FROM_USER">%1$d</xliff:g> אל <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"סגירה"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-ja/strings.xml b/packages/CarSystemUI/res/values-ja/strings.xml
index 1448675..9cf056fd 100644
--- a/packages/CarSystemUI/res/values-ja/strings.xml
+++ b/packages/CarSystemUI/res/values-ja/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"どのユーザーも他のすべてのユーザーに代わってアプリを更新できます。"</string>
<string name="car_loading_profile" msgid="4507385037552574474">"読み込んでいます"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ユーザーを読み込んでいます(<xliff:g id="FROM_USER">%1$d</xliff:g>~<xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"閉じる"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-ka/strings.xml b/packages/CarSystemUI/res/values-ka/strings.xml
index 0fef7e5..7d62c62 100644
--- a/packages/CarSystemUI/res/values-ka/strings.xml
+++ b/packages/CarSystemUI/res/values-ka/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"ნებისმიერ მომხმარებელს შეუძლია აპები ყველა სხვა მომხმარებლისათვის განაახლოს."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"იტვირთება"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"იტვირთება მომხმარებელი (<xliff:g id="FROM_USER">%1$d</xliff:g>-დან <xliff:g id="TO_USER">%2$d</xliff:g>-მდე)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"დახურვა"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-kk/strings.xml b/packages/CarSystemUI/res/values-kk/strings.xml
index a4cf787..2dd1b66 100644
--- a/packages/CarSystemUI/res/values-kk/strings.xml
+++ b/packages/CarSystemUI/res/values-kk/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Кез келген пайдаланушы қолданбаларды басқа пайдаланушылар үшін жаңарта алады."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Жүктелуде"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Пайдаланушы профилі жүктелуде (<xliff:g id="FROM_USER">%1$d</xliff:g> – <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Жабу"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-km/strings.xml b/packages/CarSystemUI/res/values-km/strings.xml
index 7b9a093..709cfe5 100644
--- a/packages/CarSystemUI/res/values-km/strings.xml
+++ b/packages/CarSystemUI/res/values-km/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"អ្នកប្រើប្រាស់ណាក៏អាចដំឡើងកំណែកម្មវិធីសម្រាប់អ្នកប្រើប្រាស់ទាំងអស់ផ្សេងទៀតបានដែរ។"</string>
<string name="car_loading_profile" msgid="4507385037552574474">"កំពុងផ្ទុក"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"កំពុងផ្ទុកអ្នកប្រើប្រាស់ (ពី <xliff:g id="FROM_USER">%1$d</xliff:g> ដល់ <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"បិទ"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-ko/strings.xml b/packages/CarSystemUI/res/values-ko/strings.xml
index b570c5c..17a2466 100644
--- a/packages/CarSystemUI/res/values-ko/strings.xml
+++ b/packages/CarSystemUI/res/values-ko/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"누구나 다른 모든 사용자를 위해 앱을 업데이트할 수 있습니다."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"로드 중"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"사용자 로드 중(<xliff:g id="FROM_USER">%1$d</xliff:g>님에서 <xliff:g id="TO_USER">%2$d</xliff:g>님으로)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"닫기"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-ky/strings.xml b/packages/CarSystemUI/res/values-ky/strings.xml
index c66b34f..dd9225a 100644
--- a/packages/CarSystemUI/res/values-ky/strings.xml
+++ b/packages/CarSystemUI/res/values-ky/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Колдонмолорду бир колдонуучу калган бардык колдонуучулар үчүн да жаңырта алат."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Жүктөлүүдө"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Колдонуучу тууралуу маалымат жүктөлүүдө (<xliff:g id="FROM_USER">%1$d</xliff:g> – <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Жабуу"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-lo/strings.xml b/packages/CarSystemUI/res/values-lo/strings.xml
index 2bf19e0..bc94a51 100644
--- a/packages/CarSystemUI/res/values-lo/strings.xml
+++ b/packages/CarSystemUI/res/values-lo/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"ຜູ້ໃຊ້ຕ່າງໆສາມາດອັບເດດແອັບສຳລັບຜູ້ໃຊ້ອື່ນທັງໝົດໄດ້."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"ກຳລັງໂຫຼດ"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ກຳລັງໂຫຼດຜູ້ໃຊ້ (ຈາກ <xliff:g id="FROM_USER">%1$d</xliff:g> ໄປຍັງ <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"ປິດ"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-lt/strings.xml b/packages/CarSystemUI/res/values-lt/strings.xml
index 1cae1e9..a47ad59 100644
--- a/packages/CarSystemUI/res/values-lt/strings.xml
+++ b/packages/CarSystemUI/res/values-lt/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Bet kuris naudotojas gali atnaujinti visų kitų naudotojų programas."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Įkeliama"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Įkeliamas naudotojo profilis (<xliff:g id="FROM_USER">%1$d</xliff:g> – <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Uždaryti"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-lv/strings.xml b/packages/CarSystemUI/res/values-lv/strings.xml
index 62b8bf8..cb7c8b9 100644
--- a/packages/CarSystemUI/res/values-lv/strings.xml
+++ b/packages/CarSystemUI/res/values-lv/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Ikviens lietotājs var atjaunināt lietotnes visu lietotāju vārdā."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Notiek ielāde…"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Notiek lietotāja profila ielāde (<xliff:g id="FROM_USER">%1$d</xliff:g>–<xliff:g id="TO_USER">%2$d</xliff:g>)…"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Aizvērt"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-mk/strings.xml b/packages/CarSystemUI/res/values-mk/strings.xml
index 3e7ad63..cd2ae97 100644
--- a/packages/CarSystemUI/res/values-mk/strings.xml
+++ b/packages/CarSystemUI/res/values-mk/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Секој корисник може да ажурира апликации за сите други корисници."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Се вчитува"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Се вчитува корисникот (од <xliff:g id="FROM_USER">%1$d</xliff:g> до <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Затвори"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-mn/strings.xml b/packages/CarSystemUI/res/values-mn/strings.xml
index 45921d2..33bcd27 100644
--- a/packages/CarSystemUI/res/values-mn/strings.xml
+++ b/packages/CarSystemUI/res/values-mn/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Бусад бүх хэрэглэгчийн аппыг дурын хэрэглэгч шинэчлэх боломжтой."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Ачаалж байна"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Хэрэглэгчийг ачаалж байна (<xliff:g id="FROM_USER">%1$d</xliff:g>-с <xliff:g id="TO_USER">%2$d</xliff:g> хүртэл)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Хаах"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-ms/strings.xml b/packages/CarSystemUI/res/values-ms/strings.xml
index 1a43d9c..0bb683b 100644
--- a/packages/CarSystemUI/res/values-ms/strings.xml
+++ b/packages/CarSystemUI/res/values-ms/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Mana-mana pengguna boleh mengemas kini apl untuk semua pengguna lain."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Memuatkan"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Memuatkan pengguna (daripada <xliff:g id="FROM_USER">%1$d</xliff:g> hingga <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Tutup"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-my/strings.xml b/packages/CarSystemUI/res/values-my/strings.xml
index 4f3922b..4e7ca39 100644
--- a/packages/CarSystemUI/res/values-my/strings.xml
+++ b/packages/CarSystemUI/res/values-my/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"မည်သူမဆို အသုံးပြုသူအားလုံးအတွက် အက်ပ်များကို အပ်ဒိတ်လုပ်နိုင်သည်။"</string>
<string name="car_loading_profile" msgid="4507385037552574474">"ဖွင့်နေသည်"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"အသုံးပြုသူကို ဖွင့်နေသည် (<xliff:g id="FROM_USER">%1$d</xliff:g> မှ <xliff:g id="TO_USER">%2$d</xliff:g> သို့)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"ပိတ်ရန်"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-nb/strings.xml b/packages/CarSystemUI/res/values-nb/strings.xml
index 5b6166f..0b2856f 100644
--- a/packages/CarSystemUI/res/values-nb/strings.xml
+++ b/packages/CarSystemUI/res/values-nb/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Alle brukere kan oppdatere apper for alle andre brukere."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Laster inn"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Laster inn brukeren (fra <xliff:g id="FROM_USER">%1$d</xliff:g> til <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Lukk"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-nl/strings.xml b/packages/CarSystemUI/res/values-nl/strings.xml
index d79f2b1..4765f71 100644
--- a/packages/CarSystemUI/res/values-nl/strings.xml
+++ b/packages/CarSystemUI/res/values-nl/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Elke gebruiker kan apps updaten voor alle andere gebruikers"</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Laden"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Gebruiker laden (van <xliff:g id="FROM_USER">%1$d</xliff:g> naar <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Sluiten"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-pl/strings.xml b/packages/CarSystemUI/res/values-pl/strings.xml
index dd8c189..52b90f1 100644
--- a/packages/CarSystemUI/res/values-pl/strings.xml
+++ b/packages/CarSystemUI/res/values-pl/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Każdy użytkownik może aktualizować aplikacje wszystkich innych użytkowników."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Ładuję"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Ładuję użytkownika (od <xliff:g id="FROM_USER">%1$d</xliff:g> do <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Zamknij"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-pt-rPT/strings.xml b/packages/CarSystemUI/res/values-pt-rPT/strings.xml
index c7f5ecf..2dffa17 100644
--- a/packages/CarSystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/CarSystemUI/res/values-pt-rPT/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Qualquer utilizador pode atualizar apps para todos os outros utilizadores."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"A carregar…"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"A carregar o utilizador (de <xliff:g id="FROM_USER">%1$d</xliff:g> para <xliff:g id="TO_USER">%2$d</xliff:g>)…"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Fechar"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-pt/strings.xml b/packages/CarSystemUI/res/values-pt/strings.xml
index 0712fb8..a7c44d2 100644
--- a/packages/CarSystemUI/res/values-pt/strings.xml
+++ b/packages/CarSystemUI/res/values-pt/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Qualquer usuário pode atualizar apps para os demais usuários."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Carregando"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Carregando usuário (de <xliff:g id="FROM_USER">%1$d</xliff:g> para <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Fechar"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-ro/strings.xml b/packages/CarSystemUI/res/values-ro/strings.xml
index 60fd4fe..1a4e71d 100644
--- a/packages/CarSystemUI/res/values-ro/strings.xml
+++ b/packages/CarSystemUI/res/values-ro/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Orice utilizator poate actualiza aplicațiile pentru toți ceilalți utilizatori."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Se încarcă"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Se încarcă utilizatorul (de la <xliff:g id="FROM_USER">%1$d</xliff:g> la <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Închideți"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-ru/strings.xml b/packages/CarSystemUI/res/values-ru/strings.xml
index a043d24..330ba2f 100644
--- a/packages/CarSystemUI/res/values-ru/strings.xml
+++ b/packages/CarSystemUI/res/values-ru/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Любой пользователь устройства может обновлять приложения для всех аккаунтов."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Загрузка…"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Загрузка профиля пользователя (с <xliff:g id="FROM_USER">%1$d</xliff:g> по <xliff:g id="TO_USER">%2$d</xliff:g>)…"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Закрыть"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-si/strings.xml b/packages/CarSystemUI/res/values-si/strings.xml
index e14f64a..6391d28 100644
--- a/packages/CarSystemUI/res/values-si/strings.xml
+++ b/packages/CarSystemUI/res/values-si/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"සියලුම අනෙක් පරිශීලකයින් සඳහා ඕනෑම පරිශීලකයෙකුට යෙදුම් යාවත්කාලීන කළ හැක."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"පූරණය වෙමින්"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"පරිශීලකයා පූරණය වෙමින් (<xliff:g id="FROM_USER">%1$d</xliff:g> සිට <xliff:g id="TO_USER">%2$d</xliff:g> වෙත)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"වසන්න"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-sk/strings.xml b/packages/CarSystemUI/res/values-sk/strings.xml
index 8f98983..b100a5d4 100644
--- a/packages/CarSystemUI/res/values-sk/strings.xml
+++ b/packages/CarSystemUI/res/values-sk/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Ktorýkoľvek používateľ môže aktualizovať aplikácie všetkých ostatných používateľov."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Načítava sa"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Načítava sa používateľ (predchádzajúci: <xliff:g id="FROM_USER">%1$d</xliff:g>, nasledujúci: <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Zavrieť"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-sl/strings.xml b/packages/CarSystemUI/res/values-sl/strings.xml
index 6b47184..b67002b 100644
--- a/packages/CarSystemUI/res/values-sl/strings.xml
+++ b/packages/CarSystemUI/res/values-sl/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Vsak uporabnik lahko posodobi aplikacije za vse druge uporabnike."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Nalaganje"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Nalaganje uporabnika (od uporabnika <xliff:g id="FROM_USER">%1$d</xliff:g> do uporabnika <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Zapri"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-sq/strings.xml b/packages/CarSystemUI/res/values-sq/strings.xml
index 18ea401..d19e158 100644
--- a/packages/CarSystemUI/res/values-sq/strings.xml
+++ b/packages/CarSystemUI/res/values-sq/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Çdo përdorues mund t\'i përditësojë aplikacionet për të gjithë përdoruesit e tjerë."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Po ngarkohet"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Përdoruesi po ngarkohet (nga <xliff:g id="FROM_USER">%1$d</xliff:g> te <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Mbyll"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-sr/strings.xml b/packages/CarSystemUI/res/values-sr/strings.xml
index 878a1c1..a5fb5b4 100644
--- a/packages/CarSystemUI/res/values-sr/strings.xml
+++ b/packages/CarSystemUI/res/values-sr/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Сваки корисник може да ажурира апликације за све остале кориснике."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Учитава се"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Профил корисника се учитава (из<xliff:g id="FROM_USER">%1$d</xliff:g> у <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Затвори"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-sv/strings.xml b/packages/CarSystemUI/res/values-sv/strings.xml
index 905dd44..8a942d6 100644
--- a/packages/CarSystemUI/res/values-sv/strings.xml
+++ b/packages/CarSystemUI/res/values-sv/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Alla användare kan uppdatera appar för andra användare."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Läser in"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Läser in användare (från <xliff:g id="FROM_USER">%1$d</xliff:g> till <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Stäng"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-sw/strings.xml b/packages/CarSystemUI/res/values-sw/strings.xml
index 3cb6098..be03373 100644
--- a/packages/CarSystemUI/res/values-sw/strings.xml
+++ b/packages/CarSystemUI/res/values-sw/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Mtumiaji yeyote anaweza kusasisha programu za watumiaji wengine."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Inapakia"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Inapakia wasifu wa mtumiaji (kutoka <xliff:g id="FROM_USER">%1$d</xliff:g> kuwa <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Funga"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-ta/strings.xml b/packages/CarSystemUI/res/values-ta/strings.xml
index de52ede..a82a2f8 100644
--- a/packages/CarSystemUI/res/values-ta/strings.xml
+++ b/packages/CarSystemUI/res/values-ta/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"எந்தப் பயனரும் பிற பயனர்கள் சார்பாக ஆப்ஸைப் புதுப்பிக்க முடியும்."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"ஏற்றுகிறது"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"பயனர் தகவலை ஏற்றுகிறது (<xliff:g id="FROM_USER">%1$d</xliff:g>லிருந்து <xliff:g id="TO_USER">%2$d</xliff:g> வரை)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"மூடுக"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-th/strings.xml b/packages/CarSystemUI/res/values-th/strings.xml
index 8f3012b..dacf605 100644
--- a/packages/CarSystemUI/res/values-th/strings.xml
+++ b/packages/CarSystemUI/res/values-th/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"ผู้ใช้ทุกคนจะอัปเดตแอปให้แก่ผู้ใช้คนอื่นๆ ได้"</string>
<string name="car_loading_profile" msgid="4507385037552574474">"กำลังโหลด"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"กำลังโหลดผู้ใช้ (จาก <xliff:g id="FROM_USER">%1$d</xliff:g> ถึง <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"ปิด"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-tl/strings.xml b/packages/CarSystemUI/res/values-tl/strings.xml
index 5b0e3b3..89d8cd2 100644
--- a/packages/CarSystemUI/res/values-tl/strings.xml
+++ b/packages/CarSystemUI/res/values-tl/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Puwedeng i-update ng sinumang user ang mga app para sa lahat ng iba pang user."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Naglo-load"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Nilo-load ang user (mula kay <xliff:g id="FROM_USER">%1$d</xliff:g> papunta kay <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Isara"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-tr/strings.xml b/packages/CarSystemUI/res/values-tr/strings.xml
index 81fa01c..36bf694 100644
--- a/packages/CarSystemUI/res/values-tr/strings.xml
+++ b/packages/CarSystemUI/res/values-tr/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Herhangi bir kullanıcı, diğer tüm kullanıcılar için uygulamaları güncelleyebilir."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Yükleniyor"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Kullanıcı yükleniyor (<xliff:g id="FROM_USER">%1$d</xliff:g> kullanıcısından <xliff:g id="TO_USER">%2$d</xliff:g> kullanıcısına)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Kapat"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-uk/strings.xml b/packages/CarSystemUI/res/values-uk/strings.xml
index b7031c6..391513f 100644
--- a/packages/CarSystemUI/res/values-uk/strings.xml
+++ b/packages/CarSystemUI/res/values-uk/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Усі користувачі можуть оновлювати додатки для решти людей."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Завантаження"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Завантаження профілю користувача (від <xliff:g id="FROM_USER">%1$d</xliff:g> до <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Закрити"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-uz/strings.xml b/packages/CarSystemUI/res/values-uz/strings.xml
index 471e459..398d1f5 100644
--- a/packages/CarSystemUI/res/values-uz/strings.xml
+++ b/packages/CarSystemUI/res/values-uz/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Qurilmaning istalgan foydalanuvchisi ilovalarni barcha hisoblar uchun yangilashi mumkin."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Yuklanmoqda"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Foydalanuvchi profili yuklanmoqda (<xliff:g id="FROM_USER">%1$d</xliff:g> – <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Yopish"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-vi/strings.xml b/packages/CarSystemUI/res/values-vi/strings.xml
index 26bdddc..f15320f 100644
--- a/packages/CarSystemUI/res/values-vi/strings.xml
+++ b/packages/CarSystemUI/res/values-vi/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Bất kỳ người dùng nào cũng có thể cập nhật ứng dụng cho tất cả những người dùng khác."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Đang tải"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Đang tải hồ sơ người dùng (từ <xliff:g id="FROM_USER">%1$d</xliff:g> sang <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Đóng"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-zh-rCN/strings.xml b/packages/CarSystemUI/res/values-zh-rCN/strings.xml
index e7ca871..a91f48c 100644
--- a/packages/CarSystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/CarSystemUI/res/values-zh-rCN/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"任何用户均可为所有其他用户更新应用。"</string>
<string name="car_loading_profile" msgid="4507385037552574474">"正在加载"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"正在加载用户(从 <xliff:g id="FROM_USER">%1$d</xliff:g> 到 <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"关闭"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-zh-rHK/strings.xml b/packages/CarSystemUI/res/values-zh-rHK/strings.xml
index 268243f..7aa6116 100644
--- a/packages/CarSystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/CarSystemUI/res/values-zh-rHK/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"任何使用者都可以為所有其他使用者更新應用程式。"</string>
<string name="car_loading_profile" msgid="4507385037552574474">"正在載入"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"正在載入使用者 (由 <xliff:g id="FROM_USER">%1$d</xliff:g> 至 <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"關閉"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-zh-rTW/strings.xml b/packages/CarSystemUI/res/values-zh-rTW/strings.xml
index 9dc0f1a..c062463 100644
--- a/packages/CarSystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/CarSystemUI/res/values-zh-rTW/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"任何使用者都能為所有其他使用者更新應用程式。"</string>
<string name="car_loading_profile" msgid="4507385037552574474">"載入中"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"正在載入使用者 (從 <xliff:g id="FROM_USER">%1$d</xliff:g> 到 <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"關閉"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values-zu/strings.xml b/packages/CarSystemUI/res/values-zu/strings.xml
index 8845ff7..2dd33d8 100644
--- a/packages/CarSystemUI/res/values-zu/strings.xml
+++ b/packages/CarSystemUI/res/values-zu/strings.xml
@@ -28,6 +28,5 @@
<string name="user_add_user_message_update" msgid="7061671307004867811">"Noma yimuphi umsebenzisi angabuyekeza izinhlelo zokusebenza zabanye abasebenzisi."</string>
<string name="car_loading_profile" msgid="4507385037552574474">"Iyalayisha"</string>
<string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Ilayisha umsebenzisi (kusuka ku-<xliff:g id="FROM_USER">%1$d</xliff:g> kuya ku-<xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
- <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) -->
- <skip />
+ <string name="rear_view_camera_close_button_text" msgid="8430918817320533693">"Vala"</string>
</resources>
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index 8667ba1..b6179ed 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -154,4 +154,13 @@
<!-- The Activity name for the Rear View Camera, if empty, the feature will be disabled. -->
<string name="config_rearViewCameraActivity" translatable="false"></string>
+
+ <!-- Whether the Notification Panel should be inset by the top system bar. -->
+ <bool name="config_notif_panel_inset_by_top_systembar" translatable="false">false</bool>
+ <!-- Whether the Notification Panel should be inset by the bottom system bar. -->
+ <bool name="config_notif_panel_inset_by_bottom_systembar" translatable="false">true</bool>
+ <!-- Whether the Notification Panel should be inset by the left system bar. -->
+ <bool name="config_notif_panel_inset_by_left_systembar" translatable="false">false</bool>
+ <!-- Whether the Notification Panel should be inset by the right system bar. -->
+ <bool name="config_notif_panel_inset_by_right_systembar" translatable="false">false</bool>
</resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
index 4b66069..599e69c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -49,6 +49,7 @@
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.CarServiceProvider;
import com.android.systemui.car.window.OverlayPanelViewController;
+import com.android.systemui.car.window.OverlayViewController;
import com.android.systemui.car.window.OverlayViewGlobalStateController;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -84,6 +85,11 @@
private final boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
private final NotificationVisibilityLogger mNotificationVisibilityLogger;
+ private final boolean mFitTopSystemBarInset;
+ private final boolean mFitBottomSystemBarInset;
+ private final boolean mFitLeftSystemBarInset;
+ private final boolean mFitRightSystemBarInset;
+
private float mInitialBackgroundAlpha;
private float mBackgroundAlphaDiff;
@@ -164,6 +170,15 @@
mEnableHeadsUpNotificationWhenNotificationShadeOpen = mResources.getBoolean(
com.android.car.notification.R.bool
.config_enableHeadsUpNotificationWhenNotificationShadeOpen);
+
+ mFitTopSystemBarInset = mResources.getBoolean(
+ R.bool.config_notif_panel_inset_by_top_systembar);
+ mFitBottomSystemBarInset = mResources.getBoolean(
+ R.bool.config_notif_panel_inset_by_bottom_systembar);
+ mFitLeftSystemBarInset = mResources.getBoolean(
+ R.bool.config_notif_panel_inset_by_left_systembar);
+ mFitRightSystemBarInset = mResources.getBoolean(
+ R.bool.config_notif_panel_inset_by_right_systembar);
}
// CommandQueue.Callbacks
@@ -215,8 +230,26 @@
}
@Override
- protected int getInsetTypesToFit() {
- return WindowInsets.Type.navigationBars();
+ protected int getInsetSidesToFit() {
+ int insetSidesToFit = OverlayViewController.NO_INSET_SIDE;
+
+ if (mFitTopSystemBarInset) {
+ insetSidesToFit = insetSidesToFit | WindowInsets.Side.TOP;
+ }
+
+ if (mFitBottomSystemBarInset) {
+ insetSidesToFit = insetSidesToFit | WindowInsets.Side.BOTTOM;
+ }
+
+ if (mFitLeftSystemBarInset) {
+ insetSidesToFit = insetSidesToFit | WindowInsets.Side.LEFT;
+ }
+
+ if (mFitRightSystemBarInset) {
+ insetSidesToFit = insetSidesToFit | WindowInsets.Side.RIGHT;
+ }
+
+ return insetSidesToFit;
}
@Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
index 6c3a632..b989c42 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
@@ -27,6 +27,9 @@
* Owns a {@link View} that is present in SystemUIOverlayWindow.
*/
public class OverlayViewController {
+ protected static final int INVALID_INSET_SIDE = -1;
+ protected static final int NO_INSET_SIDE = 0;
+
private final int mStubId;
private final OverlayViewGlobalStateController mOverlayViewGlobalStateController;
@@ -188,4 +191,28 @@
protected int getInsetTypesToFit() {
return statusBars();
}
+
+ /**
+ * Optionally returns the sides of enabled system bar insets to fit to the sysui overlay window
+ * when this {@link OverlayViewController} is in the foreground.
+ *
+ * For example, if the bottom and left system bars are enabled and this method returns
+ * WindowInsets.Side.LEFT, then the inset from the bottom system bar will be ignored.
+ *
+ * NOTE: By default, this method returns {@link #INVALID_INSET_SIDE}, so insets to fit are
+ * defined by {@link #getInsetTypesToFit()}, and not by this method, unless it is overridden
+ * by subclasses.
+ *
+ * NOTE: {@link #NO_INSET_SIDE} signifies no insets from any system bars will be honored. Each
+ * {@link OverlayViewController} can first take this value and add sides of the system bar
+ * insets to honor to it.
+ *
+ * NOTE: If getInsetSidesToFit is overridden to return {@link WindowInsets.Side}, it always
+ * takes precedence over {@link #getInsetTypesToFit()}. That is, the return value of {@link
+ * #getInsetTypesToFit()} will be ignored.
+ */
+ @WindowInsets.Side.InsetsSide
+ protected int getInsetSidesToFit() {
+ return INVALID_INSET_SIDE;
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
index 0da2360..10f436b 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
@@ -21,6 +21,8 @@
import android.annotation.Nullable;
import android.util.Log;
+import android.view.WindowInsets;
+import android.view.WindowInsets.Side.InsetsSide;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsController;
@@ -119,7 +121,7 @@
updateInternalsWhenShowingView(viewController);
refreshUseStableInsets();
- refreshInsetTypesToFit();
+ refreshInsetsToFit();
refreshWindowFocus();
refreshNavigationBarVisibility();
refreshStatusBarVisibility();
@@ -193,7 +195,7 @@
mZOrderVisibleSortedMap.remove(mZOrderMap.get(viewController));
refreshHighestZOrderWhenHidingView(viewController);
refreshUseStableInsets();
- refreshInsetTypesToFit();
+ refreshInsetsToFit();
refreshWindowFocus();
refreshNavigationBarVisibility();
refreshStatusBarVisibility();
@@ -255,11 +257,25 @@
mHighestZOrder == null ? false : mHighestZOrder.shouldUseStableInsets());
}
- private void refreshInsetTypesToFit() {
+ /**
+ * Refreshes the insets to fit (or honor) either by {@link InsetsType} or {@link InsetsSide}.
+ *
+ * By default, the insets to fit are defined by the {@link InsetsType}. But if an
+ * {@link OverlayViewController} overrides {@link OverlayViewController#getInsetSidesToFit()} to
+ * return an {@link InsetsSide}, then that takes precedence over {@link InsetsType}.
+ */
+ private void refreshInsetsToFit() {
if (mZOrderVisibleSortedMap.isEmpty()) {
setFitInsetsTypes(statusBars());
} else {
- setFitInsetsTypes(mHighestZOrder.getInsetTypesToFit());
+ if (mHighestZOrder.getInsetSidesToFit() != OverlayViewController.INVALID_INSET_SIDE) {
+ // First fit all system bar insets as setFitInsetsSide defines which sides of system
+ // bar insets to actually honor.
+ setFitInsetsTypes(WindowInsets.Type.systemBars());
+ setFitInsetsSides(mHighestZOrder.getInsetSidesToFit());
+ } else {
+ setFitInsetsTypes(mHighestZOrder.getInsetTypesToFit());
+ }
}
}
@@ -272,10 +288,16 @@
mSystemUIOverlayWindowController.setWindowVisible(visible);
}
+ /** Sets the insets to fit based on the {@link InsetsType} */
private void setFitInsetsTypes(@InsetsType int types) {
mSystemUIOverlayWindowController.setFitInsetsTypes(types);
}
+ /** Sets the insets to fit based on the {@link InsetsSide} */
+ private void setFitInsetsSides(@InsetsSide int sides) {
+ mSystemUIOverlayWindowController.setFitInsetsSides(sides);
+ }
+
/**
* Sets the {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flag of the
* sysui overlay window.
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
index b22de84..0e73f25 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
@@ -120,6 +120,13 @@
updateWindow();
}
+ /** Sets the sides of system bar insets to fit. Note: This should be rarely used. */
+ public void setFitInsetsSides(@WindowInsets.Side.InsetsSide int sides) {
+ mLpChanged.setFitInsetsSides(sides);
+ mLpChanged.setFitInsetsIgnoringVisibility(mUsingStableInsets);
+ updateWindow();
+ }
+
/** Sets the window to the visible state. */
public void setWindowVisible(boolean visible) {
mVisible = visible;
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
index 294aa0d..9c2931a 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
@@ -33,6 +33,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
+import android.view.WindowInsets;
import android.view.WindowInsetsController;
import androidx.test.filters.SmallTest;
@@ -887,6 +888,65 @@
verify(mOverlayViewController2, never()).inflate(mBaseLayout);
}
+ @Test
+ public void showView_setInsetsToFitByType_setsFitInsetsType() {
+ int insetTypeToFit = WindowInsets.Type.navigationBars();
+ setupOverlayViewController1();
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(insetTypeToFit);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(insetTypeToFit);
+ }
+
+ @Test
+ public void refreshInsetsToFit_setInsetsToFitBySide_setsFitInsetsSides() {
+ int insetSidesToFit = WindowInsets.Side.LEFT;
+ setupOverlayViewController1();
+ when(mOverlayViewController1.getInsetSidesToFit()).thenReturn(insetSidesToFit);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsSides(insetSidesToFit);
+ }
+
+ @Test
+ public void refreshInsetsToFit_setInsetsToFitBySideUsed_firstFitsAllSystemBars() {
+ int insetSidesToFit = WindowInsets.Side.LEFT;
+ setupOverlayViewController1();
+ when(mOverlayViewController1.getInsetSidesToFit()).thenReturn(insetSidesToFit);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsTypes(WindowInsets.Type.systemBars());
+ }
+
+ @Test
+ public void refreshInsetsToFit_bothInsetTypeAndSideDefined_insetSideTakesPrecedence() {
+ int insetTypesToFit = WindowInsets.Type.navigationBars();
+ int insetSidesToFit = WindowInsets.Side.LEFT;
+ setupOverlayViewController1();
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(insetTypesToFit);
+ when(mOverlayViewController1.getInsetSidesToFit()).thenReturn(insetSidesToFit);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController).setFitInsetsSides(insetSidesToFit);
+ }
+
+ @Test
+ public void refreshInsetsToFit_bothInsetTypeAndSideDefined_insetTypeIgnored() {
+ int insetTypesToFit = WindowInsets.Type.navigationBars();
+ int insetSidesToFit = WindowInsets.Side.LEFT;
+ setupOverlayViewController1();
+ when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(insetTypesToFit);
+ when(mOverlayViewController1.getInsetSidesToFit()).thenReturn(insetSidesToFit);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mSystemUIOverlayWindowController, never()).setFitInsetsTypes(insetTypesToFit);
+ }
+
private void setupOverlayViewController1() {
setupOverlayViewController(mOverlayViewController1, R.id.overlay_view_controller_stub_1,
R.id.overlay_view_controller_1);
@@ -913,6 +973,8 @@
}
when(overlayViewController.getLayout()).thenReturn(layout);
when(overlayViewController.isInflated()).thenReturn(true);
+ when(overlayViewController.getInsetSidesToFit()).thenReturn(
+ OverlayViewController.INVALID_INSET_SIDE);
}
private void setOverlayViewControllerAsShowing(OverlayViewController overlayViewController) {
diff --git a/packages/CompanionDeviceManager/Android.bp b/packages/CompanionDeviceManager/Android.bp
index 1453ec3..354d2c7 100644
--- a/packages/CompanionDeviceManager/Android.bp
+++ b/packages/CompanionDeviceManager/Android.bp
@@ -14,6 +14,7 @@
android_app {
name: "CompanionDeviceManager",
+ defaults: ["platform_app_defaults"],
srcs: ["src/**/*.java"],
platform_apis: true,
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 02f4457..9aa7cc4a 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -17,6 +17,7 @@
package com.android.companiondevicemanager;
import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import android.app.Activity;
import android.companion.CompanionDeviceManager;
@@ -57,6 +58,8 @@
Log.e(LOG_TAG, "About to show UI, but no devices to show");
}
+ getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+
if (getService().mRequest.isSingleDevice()) {
setContentView(R.layout.device_confirmation);
final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0);
diff --git a/packages/DynamicSystemInstallationService/Android.bp b/packages/DynamicSystemInstallationService/Android.bp
index f1a18ae..a8cf5d6 100644
--- a/packages/DynamicSystemInstallationService/Android.bp
+++ b/packages/DynamicSystemInstallationService/Android.bp
@@ -1,5 +1,6 @@
android_app {
name: "DynamicSystemInstallationService",
+ defaults: ["platform_app_defaults"],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/EncryptedLocalTransport/Android.bp b/packages/EncryptedLocalTransport/Android.bp
index dd30ad1..00e9c71 100644
--- a/packages/EncryptedLocalTransport/Android.bp
+++ b/packages/EncryptedLocalTransport/Android.bp
@@ -16,6 +16,7 @@
android_app {
name: "EncryptedLocalTransport",
+ defaults: ["platform_app_defaults"],
srcs: ["src/**/*.java"],
optimize: {
proguard_flags_files: ["proguard.flags"],
diff --git a/packages/ExtShared/Android.bp b/packages/ExtShared/Android.bp
index a9823b9..279ac9d 100644
--- a/packages/ExtShared/Android.bp
+++ b/packages/ExtShared/Android.bp
@@ -14,6 +14,7 @@
android_app {
name: "ExtShared",
+ defaults: ["platform_app_defaults"],
srcs: ["src/**/*.java"],
sdk_version: "current",
certificate: "platform",
diff --git a/packages/ExternalStorageProvider/Android.bp b/packages/ExternalStorageProvider/Android.bp
index 973fef3..f1e6299 100644
--- a/packages/ExternalStorageProvider/Android.bp
+++ b/packages/ExternalStorageProvider/Android.bp
@@ -1,5 +1,6 @@
android_app {
name: "ExternalStorageProvider",
+ defaults: ["platform_app_defaults"],
manifest: "AndroidManifest.xml",
diff --git a/packages/FakeOemFeatures/Android.bp b/packages/FakeOemFeatures/Android.bp
index b265158..b63e3a1 100644
--- a/packages/FakeOemFeatures/Android.bp
+++ b/packages/FakeOemFeatures/Android.bp
@@ -1,5 +1,6 @@
android_app {
name: "FakeOemFeatures",
+ defaults: ["platform_app_defaults"],
srcs: ["src/**/*.java"],
platform_apis: true,
certificate: "platform",
diff --git a/packages/FusedLocation/Android.bp b/packages/FusedLocation/Android.bp
index 242caab..ada463a 100644
--- a/packages/FusedLocation/Android.bp
+++ b/packages/FusedLocation/Android.bp
@@ -14,6 +14,7 @@
android_app {
name: "FusedLocation",
+ defaults: ["platform_app_defaults"],
srcs: ["src/**/*.java"],
libs: ["com.android.location.provider"],
platform_apis: true,
@@ -45,4 +46,4 @@
platform_apis: true,
certificate: "platform",
test_suites: ["device-tests"]
-}
\ No newline at end of file
+}
diff --git a/packages/InputDevices/Android.bp b/packages/InputDevices/Android.bp
index 7532aea..5afbe72 100644
--- a/packages/InputDevices/Android.bp
+++ b/packages/InputDevices/Android.bp
@@ -14,6 +14,7 @@
android_app {
name: "InputDevices",
+ defaults: ["platform_app_defaults"],
srcs: [
"**/*.java",
diff --git a/packages/LocalTransport/Android.bp b/packages/LocalTransport/Android.bp
index 2c990fe..9a98a86 100644
--- a/packages/LocalTransport/Android.bp
+++ b/packages/LocalTransport/Android.bp
@@ -16,6 +16,7 @@
android_app {
name: "LocalTransport",
+ defaults: ["platform_app_defaults"],
srcs: ["src/**/*.java"],
optimize: {
proguard_flags_files: ["proguard.flags"],
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
index 75bd32e..4d9c675 100644
--- a/packages/PackageInstaller/Android.bp
+++ b/packages/PackageInstaller/Android.bp
@@ -14,6 +14,7 @@
android_app {
name: "PackageInstaller",
+ defaults: ["platform_app_defaults"],
srcs: ["src/**/*.java"],
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index be778e9..2674eaf 100755
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -339,8 +339,9 @@
broadcastIntent.putExtra(UninstallFinish.EXTRA_APP_LABEL, label);
broadcastIntent.putExtra(UninstallFinish.EXTRA_UNINSTALL_ID, uninstallId);
- PendingIntent pendingIntent = PendingIntent.getBroadcast(this, uninstallId,
- broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent pendingIntent =
+ PendingIntent.getBroadcast(this, uninstallId, broadcastIntent,
+ PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
NotificationChannel uninstallingChannel = new NotificationChannel(UNINSTALLING_CHANNEL,
diff --git a/packages/PrintRecommendationService/Android.bp b/packages/PrintRecommendationService/Android.bp
index 6d28bdb..d368f3c 100644
--- a/packages/PrintRecommendationService/Android.bp
+++ b/packages/PrintRecommendationService/Android.bp
@@ -14,6 +14,7 @@
android_app {
name: "PrintRecommendationService",
+ defaults: ["platform_app_defaults"],
srcs: ["src/**/*.java"],
sdk_version: "system_current",
static_libs: [
diff --git a/packages/PrintSpooler/Android.bp b/packages/PrintSpooler/Android.bp
index c40a81791..d38fd02 100644
--- a/packages/PrintSpooler/Android.bp
+++ b/packages/PrintSpooler/Android.bp
@@ -14,6 +14,7 @@
android_app {
name: "PrintSpooler",
+ defaults: ["platform_app_defaults"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 0deb927..ebbe996 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -27,7 +27,7 @@
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Onemogućeno"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP konfiguracija je otkazala"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Nije povezano zbog lošeg kvaliteta mreže"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Wi-Fi veza je otkazala"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi veza je otkazala"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problem sa potvrdom identiteta"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Povezivanje nije uspelo"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Povezivanje sa „<xliff:g id="AP_NAME">%1$s</xliff:g>“ nije uspelo"</string>
@@ -131,12 +131,12 @@
<string name="bluetooth_hearingaid_right_pairing_message" msgid="2655347721696331048">"Uparivanje desnog slušnog aparata…"</string>
<string name="bluetooth_hearingaid_left_battery_level" msgid="7375621694748104876">"Levi – nivo baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_hearingaid_right_battery_level" msgid="1850094448499089312">"Desni – nivo baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi je isključen."</string>
- <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi veza je prekinuta."</string>
- <string name="accessibility_wifi_one_bar" msgid="6025652717281815212">"Wi-Fi signal ima jednu crtu."</string>
- <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi signal ima dve crte."</string>
- <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi signal ima tri crte."</string>
- <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi signal je najjači."</string>
+ <string name="accessibility_wifi_off" msgid="1195445715254137155">"WiFi je isključen."</string>
+ <string name="accessibility_no_wifi" msgid="5297119459491085771">"WiFi veza je prekinuta."</string>
+ <string name="accessibility_wifi_one_bar" msgid="6025652717281815212">"WiFi signal ima jednu crtu."</string>
+ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"WiFi signal ima dve crte."</string>
+ <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"WiFi signal ima tri crte."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"WiFi signal je najjači."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Otvorena mreža"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Bezbedna mreža"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
@@ -232,7 +232,7 @@
<string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP adresa i port"</string>
<string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Skeniraj QR kôd"</string>
<string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Uparite uređaj pomoću Wi‑Fi mreže tako što ćete skenirati QR kôd"</string>
- <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Povežite se na Wi-Fi mrežu"</string>
+ <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Povežite se na WiFi mrežu"</string>
<string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, otklanjanje grešaka, programer"</string>
<string name="bugreport_in_power" msgid="8664089072534638709">"Prečica za izveštaj o greškama"</string>
<string name="bugreport_in_power_summary" msgid="1885529649381831775">"Prikaži dugme u meniju napajanja za pravljenje izveštaja o greškama"</string>
@@ -250,7 +250,7 @@
<string name="debug_networking_category" msgid="6829757985772659599">"Umrežavanje"</string>
<string name="wifi_display_certification" msgid="1805579519992520381">"Sertifikacija bežičnog ekrana"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Omogući detaljniju evidenciju za Wi‑Fi"</string>
- <string name="wifi_scan_throttling" msgid="2985624788509913617">"Usporavanje Wi-Fi skeniranja"</string>
+ <string name="wifi_scan_throttling" msgid="2985624788509913617">"Usporavanje WiFi skeniranja"</string>
<string name="wifi_enhanced_mac_randomization" msgid="5437378364995776979">"Nasumično MAC razvrstavanje po Wi‑Fi‑ju"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobilni podaci su uvek aktivni"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Hardversko ubrzanje privezivanja"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 7c8dc24..8c55328 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -97,7 +97,7 @@
<string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"श्रवण यंत्रांशी कनेक्ट केले आहे"</string>
<string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"मीडिया ऑडिओवर कनेक्ट केले"</string>
<string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"फोन ऑडिओ वर कनेक्ट केले"</string>
- <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"फाईल स्थानांतर सर्व्हरवर कनेक्ट केले"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"फाइल स्थानांतर सर्व्हरवर कनेक्ट केले"</string>
<string name="bluetooth_map_profile_summary_connected" msgid="4141725591784669181">"नकाशाशी कनेक्ट केले"</string>
<string name="bluetooth_sap_profile_summary_connected" msgid="1280297388033001037">"SAP शी कनेक्ट केले"</string>
<string name="bluetooth_opp_profile_summary_not_connected" msgid="3959741824627764954">"फाइल स्थानांतर सर्व्हरशी कनेक्ट केले नाही"</string>
@@ -109,7 +109,7 @@
<string name="bluetooth_sap_profile_summary_use_for" msgid="6204902866176714046">"SIM प्रवेशासाठी वापरा"</string>
<string name="bluetooth_a2dp_profile_summary_use_for" msgid="7324694226276491807">"मीडिया ऑडिओसाठी वापरा"</string>
<string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"फोन ऑडिओसाठी वापरा"</string>
- <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"फाईल स्थानांतरणासाठी वापरा"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"फाइल स्थानांतरणासाठी वापरा"</string>
<string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"इनपुट साठी वापरा"</string>
<string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"श्रवण यंत्रांसाठी वापरा"</string>
<string name="bluetooth_pairing_accept" msgid="2054232610815498004">"पेअर करा"</string>
@@ -268,8 +268,8 @@
<string name="bluetooth_select_a2dp_codec_type_help_info" msgid="8647200416514412338">"फोन किंवा हेडसेटला सपोर्ट करत नाही म्हणजे ती निकामी झाली आहे"</string>
<string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="6253965294594390806">"प्रति पॅटर्न ब्लूटूध ऑडिओ बिट"</string>
<string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4898693684282596143">"ब्लूटूथ ऑडिओ Codec ट्रिगर करा\nनिवड: बिट प्रति नमुना"</string>
- <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="364277285688014427">"ब्लूटूथ ऑडिओ चॅनेल मोड"</string>
- <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="2076949781460359589">"ब्लूटूथ ऑडिओ Codec ट्रिगर करा\nनिवड: चॅनेल मोड"</string>
+ <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="364277285688014427">"ब्लूटूथ ऑडिओ चॅनल मोड"</string>
+ <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="2076949781460359589">"ब्लूटूथ ऑडिओ Codec ट्रिगर करा\nनिवड: चॅनल मोड"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3233402355917446304">"ब्लूटूथ ऑडिओ LDAC कोडेक: प्लेबॅक गुणवत्ता"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="7274396574659784285">"ब्लूटूथ ऑडिओ LDAC\nकोडेक निवड ट्रिगर करा: प्लेबॅक गुणवत्ता"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"स्ट्रीमिंग: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
@@ -370,7 +370,7 @@
<string name="app_process_limit_title" msgid="8361367869453043007">"पार्श्वभूमी प्रक्रिया मर्यादा"</string>
<string name="show_all_anrs" msgid="9160563836616468726">"बॅकग्राउंड ANR दाखवा"</string>
<string name="show_all_anrs_summary" msgid="8562788834431971392">"बॅकग्राउंड अॅप्ससाठी अॅप प्रतिसाद देत नाही दाखवते"</string>
- <string name="show_notification_channel_warnings" msgid="3448282400127597331">"सूचना चॅनेल चेतावण्या दाखवा"</string>
+ <string name="show_notification_channel_warnings" msgid="3448282400127597331">"सूचना चॅनल चेतावण्या दाखवा"</string>
<string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"एखादे अॅप वैध चॅनेलशिवाय सूचना पोस्ट करते तेव्हा स्क्रीनवर चेतावणी देते"</string>
<string name="force_allow_on_external" msgid="9187902444231637880">"बाह्यवर ॲप्सना अनुमती देण्याची सक्ती करा"</string>
<string name="force_allow_on_external_summary" msgid="8525425782530728238">"manifest मूल्यांकडे दुर्लक्ष करून, कोणत्याही ॲपला बाह्य स्टोरेजवर लेखन केले जाण्यासाठी पात्र बनविते"</string>
@@ -404,11 +404,11 @@
<string name="select_webview_provider_title" msgid="3917815648099445503">"वेबदृश्य अंमलबजावणी"</string>
<string name="select_webview_provider_dialog_title" msgid="2444261109877277714">"वेबदृश्य अंमलबजावणी सेट करा"</string>
<string name="select_webview_provider_toast_text" msgid="8512254949169359848">"ही निवड यापुढे वैध असणार नाही. पुन्हा प्रयत्न करा."</string>
- <string name="convert_to_file_encryption" msgid="2828976934129751818">"फाईल कूटबद्धीकरणावर रूपांतरित करा"</string>
+ <string name="convert_to_file_encryption" msgid="2828976934129751818">"फाइल कूटबद्धीकरणावर रूपांतरित करा"</string>
<string name="convert_to_file_encryption_enabled" msgid="840757431284311754">"रूपांतरित करा..."</string>
- <string name="convert_to_file_encryption_done" msgid="8965831011811180627">"फाईल आधीपासून एंक्रिप्ट होती"</string>
- <string name="title_convert_fbe" msgid="5780013350366495149">"फाईल आधारित कूटबद्धीकरणावर रूपांतरित करणे"</string>
- <string name="convert_to_fbe_warning" msgid="34294381569282109">"फाईल आधारित कूटबद्धीकरणावर डेटा विभाजक रूपांतरित करा.\n !!चेतावणी!! हे आपल्या सर्व डेटास मिटवेल.\n हे वैशिष्ट्य अल्फा आहे आणि कदाचित योग्यरित्या कार्य करू शकत नाही.\n सुरू ठेवण्यासाठी \'पुसा आणि रूपांतरित करा...\' दाबा."</string>
+ <string name="convert_to_file_encryption_done" msgid="8965831011811180627">"फाइल आधीपासून एंक्रिप्ट होती"</string>
+ <string name="title_convert_fbe" msgid="5780013350366495149">"फाइल आधारित कूटबद्धीकरणावर रूपांतरित करणे"</string>
+ <string name="convert_to_fbe_warning" msgid="34294381569282109">"फाइल आधारित कूटबद्धीकरणावर डेटा विभाजक रूपांतरित करा.\n !!चेतावणी!! हे आपल्या सर्व डेटास मिटवेल.\n हे वैशिष्ट्य अल्फा आहे आणि कदाचित योग्यरित्या कार्य करू शकत नाही.\n सुरू ठेवण्यासाठी \'पुसा आणि रूपांतरित करा...\' दाबा."</string>
<string name="button_convert_fbe" msgid="1159861795137727671">"पुसा आणि रुपांतरित करा..."</string>
<string name="picture_color_mode" msgid="1013807330552931903">"चित्र रंग मोड"</string>
<string name="picture_color_mode_desc" msgid="151780973768136200">"sRGB वापरा"</string>
@@ -525,7 +525,7 @@
<string name="accessor_expires_text" msgid="4625619273236786252">"भाडेपट्टी <xliff:g id="DATE">%s</xliff:g> रोजी एक्स्पायर होईल"</string>
<string name="delete_blob_text" msgid="2819192607255625697">"शेअर केलेला डेटा हटवा"</string>
<string name="delete_blob_confirmation_text" msgid="7807446938920827280">"तुम्हाला नक्की हा शेअर केलेला डेटा हटवायचा आहे का?"</string>
- <string name="user_add_user_item_summary" msgid="5748424612724703400">"वापरकर्त्यांकडे त्यांचे स्वत:चे अॅप्स आणि सामग्री आहे"</string>
+ <string name="user_add_user_item_summary" msgid="5748424612724703400">"वापरकर्त्यांकडे त्यांचे स्वत:चे अॅप्स आणि आशय आहे"</string>
<string name="user_add_profile_item_summary" msgid="5418602404308968028">"तुम्ही आपल्या खात्यावरुन अॅप्स आणि सामग्रीमध्ये प्रवेश करण्यास प्रतिबंध करु शकता"</string>
<string name="user_add_user_item_title" msgid="2394272381086965029">"वापरकर्ता"</string>
<string name="user_add_profile_item_title" msgid="3111051717414643029">"प्रतिबंधित प्रोफाईल"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index bb59bd1..18cf808 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -27,7 +27,7 @@
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Онемогућено"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP конфигурација је отказала"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Није повезано због лошег квалитета мреже"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Wi-Fi веза је отказала"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi веза је отказала"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Проблем са потврдом идентитета"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Повезивање није успело"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Повезивање са „<xliff:g id="AP_NAME">%1$s</xliff:g>“ није успело"</string>
@@ -131,12 +131,12 @@
<string name="bluetooth_hearingaid_right_pairing_message" msgid="2655347721696331048">"Упаривање десног слушног апарата…"</string>
<string name="bluetooth_hearingaid_left_battery_level" msgid="7375621694748104876">"Леви – ниво батерије је <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_hearingaid_right_battery_level" msgid="1850094448499089312">"Десни – ниво батерије је <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi је искључен."</string>
- <string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi веза је прекинута."</string>
- <string name="accessibility_wifi_one_bar" msgid="6025652717281815212">"Wi-Fi сигнал има једну црту."</string>
- <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi сигнал има две црте."</string>
- <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi сигнал има три црте."</string>
- <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi сигнал је најјачи."</string>
+ <string name="accessibility_wifi_off" msgid="1195445715254137155">"WiFi је искључен."</string>
+ <string name="accessibility_no_wifi" msgid="5297119459491085771">"WiFi веза је прекинута."</string>
+ <string name="accessibility_wifi_one_bar" msgid="6025652717281815212">"WiFi сигнал има једну црту."</string>
+ <string name="accessibility_wifi_two_bars" msgid="687800024970972270">"WiFi сигнал има две црте."</string>
+ <string name="accessibility_wifi_three_bars" msgid="779895671061950234">"WiFi сигнал има три црте."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"WiFi сигнал је најјачи."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Отворена мрежа"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Безбедна мрежа"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android ОС"</string>
@@ -232,7 +232,7 @@
<string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP адреса и порт"</string>
<string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Скенирај QR кôд"</string>
<string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Упарите уређај помоћу Wi‑Fi мреже тако што ћете скенирати QR кôд"</string>
- <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Повежите се на Wi-Fi мрежу"</string>
+ <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Повежите се на WiFi мрежу"</string>
<string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, отклањање грешака, програмер"</string>
<string name="bugreport_in_power" msgid="8664089072534638709">"Пречица за извештај о грешкама"</string>
<string name="bugreport_in_power_summary" msgid="1885529649381831775">"Прикажи дугме у менију напајања за прављење извештаја о грешкама"</string>
@@ -250,7 +250,7 @@
<string name="debug_networking_category" msgid="6829757985772659599">"Умрежавање"</string>
<string name="wifi_display_certification" msgid="1805579519992520381">"Сертификација бежичног екрана"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Омогући детаљнију евиденцију за Wi‑Fi"</string>
- <string name="wifi_scan_throttling" msgid="2985624788509913617">"Успоравање Wi-Fi скенирања"</string>
+ <string name="wifi_scan_throttling" msgid="2985624788509913617">"Успоравање WiFi скенирања"</string>
<string name="wifi_enhanced_mac_randomization" msgid="5437378364995776979">"Насумично MAC разврставање по Wi‑Fi‑ју"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Мобилни подаци су увек активни"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Хардверско убрзање привезивања"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index e7b1482..197655e 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -451,7 +451,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laddas snabbt"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Laddas långsamt"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Laddar inte"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Inkopplad, kan inte laddas just nu"</string>
+ <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Ansluten, kan inte laddas just nu"</string>
<string name="battery_info_status_full" msgid="4443168946046847468">"Fullt"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Strys av administratören"</string>
<string name="disabled" msgid="8017887509554714950">"Inaktiverad"</string>
diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp
index 9d042a4..2e53478 100644
--- a/packages/SettingsProvider/Android.bp
+++ b/packages/SettingsProvider/Android.bp
@@ -1,5 +1,6 @@
android_app {
name: "SettingsProvider",
+ defaults: ["platform_app_defaults"],
resource_dirs: ["res"],
srcs: [
"src/**/*.java",
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index c1bdb56..b9e30fb 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -142,7 +142,6 @@
Settings.Secure.CHARGING_VIBRATION_ENABLED,
Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS,
Settings.Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS,
- Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL,
Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK,
Settings.Secure.UI_NIGHT_MODE,
Settings.Secure.DARK_THEME_CUSTOM_START_TIME,
@@ -176,8 +175,8 @@
Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
Settings.Secure.TAPS_APP_TO_EXIT,
Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
- Settings.Secure.PANIC_GESTURE_ENABLED,
- Settings.Secure.PANIC_SOUND_ENABLED,
+ Settings.Secure.EMERGENCY_GESTURE_ENABLED,
+ Settings.Secure.EMERGENCY_GESTURE_SOUND_ENABLED,
Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED,
Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 388bf28..721bf73 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -206,7 +206,6 @@
Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.USER_SETUP_COMPLETE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ASSIST_GESTURE_SETUP_COMPLETE, BOOLEAN_VALIDATOR);
- VALIDATORS.put(Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.TRUST_AGENTS_EXTEND_UNLOCK, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, JSON_OBJECT_VALIDATOR);
VALIDATORS.put(Secure.LOCK_SCREEN_WHEN_TRUST_LOST, BOOLEAN_VALIDATOR);
@@ -265,8 +264,8 @@
VALIDATORS.put(Secure.ONE_HANDED_MODE_TIMEOUT, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.TAPS_APP_TO_EXIT, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR);
- VALIDATORS.put(Secure.PANIC_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
- VALIDATORS.put(Secure.PANIC_SOUND_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.EMERGENCY_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.EMERGENCY_GESTURE_SOUND_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(
Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, NONE_NEGATIVE_LONG_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 90bed12..1a2c2c8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -863,9 +863,6 @@
p.end(intentFirewallToken);
dumpSetting(s, p,
- Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS,
- GlobalSettingsProto.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS);
- dumpSetting(s, p,
Settings.Global.KEEP_PROFILE_IN_BACKGROUND,
GlobalSettingsProto.KEEP_PROFILE_IN_BACKGROUND);
@@ -1494,9 +1491,6 @@
Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
GlobalSettingsProto.Webview.DATA_REDUCTION_PROXY_KEY);
dumpSetting(s, p,
- Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED,
- GlobalSettingsProto.Webview.FALLBACK_LOGIC_ENABLED);
- dumpSetting(s, p,
Settings.Global.WEBVIEW_PROVIDER,
GlobalSettingsProto.Webview.PROVIDER);
dumpSetting(s, p,
@@ -2038,11 +2032,11 @@
final long emergencyResponseToken = p.start(SecureSettingsProto.EMERGENCY_RESPONSE);
dumpSetting(s, p,
- Settings.Secure.PANIC_GESTURE_ENABLED,
- SecureSettingsProto.EmergencyResponse.PANIC_GESTURE_ENABLED);
+ Settings.Secure.EMERGENCY_GESTURE_ENABLED,
+ SecureSettingsProto.EmergencyResponse.EMERGENCY_GESTURE_ENABLED);
dumpSetting(s, p,
- Settings.Secure.PANIC_SOUND_ENABLED,
- SecureSettingsProto.EmergencyResponse.PANIC_SOUND_ENABLED);
+ Settings.Secure.EMERGENCY_GESTURE_SOUND_ENABLED,
+ SecureSettingsProto.EmergencyResponse.EMERGENCY_GESTURE_SOUND_ENABLED);
p.end(emergencyResponseToken);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 4713243..6b4629a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -309,7 +309,6 @@
Settings.Global.INSTANT_APP_DEXOPT_ENABLED,
Settings.Global.INTENT_FIREWALL_UPDATE_CONTENT_URL,
Settings.Global.INTENT_FIREWALL_UPDATE_METADATA_URL,
- Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS,
Settings.Global.KEEP_PROFILE_IN_BACKGROUND,
Settings.Global.KERNEL_CPU_THREAD_READER,
Settings.Global.LANG_ID_UPDATE_CONTENT_URL,
@@ -526,7 +525,6 @@
Settings.Global.NETWORK_ACCESS_TIMEOUT_MS,
Settings.Global.WARNING_TEMPERATURE,
Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
- Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED,
Settings.Global.WEBVIEW_MULTIPROCESS,
Settings.Global.WEBVIEW_PROVIDER,
Settings.Global.WFC_IMS_ENABLED,
diff --git a/packages/SharedStorageBackup/Android.bp b/packages/SharedStorageBackup/Android.bp
index 5380832..d02f480 100644
--- a/packages/SharedStorageBackup/Android.bp
+++ b/packages/SharedStorageBackup/Android.bp
@@ -16,6 +16,7 @@
android_app {
name: "SharedStorageBackup",
+ defaults: ["platform_app_defaults"],
srcs: ["src/**/*.java"],
optimize: {
proguard_flags_files: ["proguard.flags"],
diff --git a/packages/Shell/Android.bp b/packages/Shell/Android.bp
index aaaf044..c873e30 100644
--- a/packages/Shell/Android.bp
+++ b/packages/Shell/Android.bp
@@ -1,5 +1,6 @@
android_app {
name: "Shell",
+ defaults: ["platform_app_defaults"],
srcs: ["src/**/*.java",":dumpstate_aidl"],
aidl: {
include_dirs: ["frameworks/native/cmds/dumpstate/binder"],
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 5f018a0..9cd69cc 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -338,6 +338,9 @@
<!-- Permissions required for CTS test - NotificationManagerTest -->
<uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" />
+ <!-- Allows overriding the system's device state from the shell -->
+ <uses-permission android:name="android.permission.CONTROL_DEVICE_STATE"/>
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SimAppDialog/Android.bp b/packages/SimAppDialog/Android.bp
index 176035f..9c0d78c 100644
--- a/packages/SimAppDialog/Android.bp
+++ b/packages/SimAppDialog/Android.bp
@@ -1,5 +1,6 @@
android_app {
name: "SimAppDialog",
+ defaults: ["platform_app_defaults"],
srcs: ["src/**/*.java"],
diff --git a/packages/SoundPicker/Android.bp b/packages/SoundPicker/Android.bp
index 3be7ca9..56e7cd1 100644
--- a/packages/SoundPicker/Android.bp
+++ b/packages/SoundPicker/Android.bp
@@ -1,5 +1,6 @@
android_app {
name: "SoundPicker",
+ defaults: ["platform_app_defaults"],
manifest: "AndroidManifest.xml",
static_libs: [
diff --git a/packages/StatementService/Android.bp b/packages/StatementService/Android.bp
index 586292e..ae37efc 100644
--- a/packages/StatementService/Android.bp
+++ b/packages/StatementService/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
android_app {
name: "StatementService",
+ defaults: ["platform_app_defaults"],
srcs: ["src/**/*.java"],
optimize: {
proguard_flags_files: ["proguard.flags"],
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 80a6257..014d73f 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -163,6 +163,7 @@
android_app {
name: "SystemUI",
+ defaults: ["platform_app_defaults"],
static_libs: [
"SystemUI-core",
],
@@ -184,5 +185,4 @@
"privapp_whitelist_com.android.systemui",
"checked-wm_shell_protolog.json",
],
-
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 4071045..ddd0dac 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -586,6 +586,8 @@
</intent-filter>
</activity>
+ <activity android:name=".people.widget.LaunchConversationActivity" />
+
<!-- People Space Widget -->
<receiver
android:name=".people.widget.PeopleSpaceWidgetProvider"
diff --git a/packages/SystemUI/docs/broadcasts.md b/packages/SystemUI/docs/broadcasts.md
index 8ec20f5..e709278 100644
--- a/packages/SystemUI/docs/broadcasts.md
+++ b/packages/SystemUI/docs/broadcasts.md
@@ -62,17 +62,17 @@
* @param executor An executor to dispatch [BroadcastReceiver.onReceive]. Pass null to use an
* executor in the main thread (default).
* @param user A user handle to determine which broadcast should be dispatched to this receiver.
- * By default, it is the user of the context (system user in SystemUI).
+ * Pass `null` to use the user of the context (system user in SystemUI).
* @throws IllegalArgumentException if the filter has other constraints that are not actions or
* categories or the filter has no actions.
*/
@JvmOverloads
-fun registerReceiver(
- BroadcastReceiver,
- IntentFilter,
- Executor? = context.mainExecutor,
- UserHandle = context.user
-) {
+open fun registerReceiver(
+ receiver: BroadcastReceiver,
+ filter: IntentFilter,
+ executor: Executor? = null,
+ user: UserHandle? = null
+)
```
All subscriptions are done with the same overloaded method. As specified in the doc, in order to pass a `UserHandle` with the default `Executor`, pass `null` for the `Executor`.
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index f22c729..3714db3 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -67,6 +67,7 @@
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
+ android:layout_marginBottom="24dp"
android:visibility="gone">
<com.android.keyguard.GradientTextClock
android:id="@+id/gradient_clock_view"
@@ -88,4 +89,15 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/clock_view" />
+
+ <com.android.systemui.statusbar.phone.NotificationIconContainer
+ android:id="@+id/left_aligned_notification_icon_container"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_shelf_height"
+ android:layout_marginTop="@dimen/widget_vertical_padding"
+ android:layout_below="@id/keyguard_status_area"
+ android:visibility="gone"
+ />
</com.android.keyguard.KeyguardClockSwitch>
diff --git a/packages/SystemUI/res/drawable/ic_check_box.xml b/packages/SystemUI/res/drawable/ic_check_box.xml
new file mode 100644
index 0000000..a8d1a65
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_check_box.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
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/checked"
+ android:state_checked="true"
+ android:drawable="@drawable/ic_check_box_blue_24dp" />
+ <item
+ android:id="@+id/unchecked"
+ android:state_checked="false"
+ android:drawable="@drawable/ic_check_box_outline_24dp" />
+</selector>
diff --git a/packages/SystemUI/res/drawable/ic_check_box_blue_24dp.xml b/packages/SystemUI/res/drawable/ic_check_box_blue_24dp.xml
new file mode 100644
index 0000000..43cae69
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_check_box_blue_24dp.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
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M19,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.9 2,-2L21,5c0,-1.1 -0.89,-2 -2,-2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"
+ android:fillColor="#4285F4"/>
+</vector>
+
diff --git a/packages/SystemUI/res/drawable/ic_check_box_outline_24dp.xml b/packages/SystemUI/res/drawable/ic_check_box_outline_24dp.xml
new file mode 100644
index 0000000..f6f453a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_check_box_outline_24dp.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
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M19,5v14H5V5h14m0,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"
+ android:fillColor="#757575"/>
+</vector>
+
diff --git a/packages/SystemUI/res/drawable/ic_speaker_group_black_24dp.xml b/packages/SystemUI/res/drawable/ic_speaker_group_black_24dp.xml
new file mode 100644
index 0000000..ae0d562
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_speaker_group_black_24dp.xml
@@ -0,0 +1,31 @@
+<!--
+ 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
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M18.2,1L9.8,1C8.81,1 8,1.81 8,2.8v14.4c0,0.99 0.81,1.79 1.8,1.79l8.4,0.01c0.99,0 1.8,-0.81 1.8,-1.8L20,2.8c0,-0.99 -0.81,-1.8 -1.8,-1.8zM14,3c1.1,0 2,0.89 2,2s-0.9,2 -2,2 -2,-0.89 -2,-2 0.9,-2 2,-2zM14,16.5c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M14,12.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M6,5H4v16c0,1.1 0.89,2 2,2h10v-2H6V5z"
+ android:fillColor="#000000"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml
index ac8b7b5..c98c3a0 100644
--- a/packages/SystemUI/res/layout/media_output_list_item.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item.xml
@@ -15,97 +15,124 @@
~ limitations under the License.
-->
-<FrameLayout
- android:id="@+id/device_container"
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/device_container"
android:layout_width="match_parent"
- android:layout_height="64dp">
-
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
<FrameLayout
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_gravity="center_vertical"
- android:layout_marginStart="16dp">
- <ImageView
- android:id="@+id/title_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"/>
- </FrameLayout>
+ android:layout_width="match_parent"
+ android:layout_height="64dp">
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginStart="68dp"
- android:ellipsize="end"
- android:maxLines="1"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="14sp"/>
+ <FrameLayout
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="16dp">
+ <ImageView
+ android:id="@+id/title_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"/>
+ </FrameLayout>
- <RelativeLayout
- android:id="@+id/two_line_layout"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:layout_marginStart="52dp"
- android:layout_marginEnd="69dp"
- android:layout_marginTop="10dp">
<TextView
- android:id="@+id/two_line_title"
+ android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="15dp"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="68dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:attr/textColorPrimary"
android:textSize="14sp"/>
- <TextView
- android:id="@+id/subtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="15dp"
- android:layout_marginBottom="7dp"
- android:layout_alignParentBottom="true"
- android:ellipsize="end"
- android:maxLines="1"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="12sp"
- android:fontFamily="roboto-regular"
- android:visibility="gone"/>
- <SeekBar
- android:id="@+id/volume_seekbar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"/>
- </RelativeLayout>
- <ProgressBar
- android:id="@+id/volume_indeterminate_progress"
- style="@*android:style/Widget.Material.ProgressBar.Horizontal"
- android:layout_width="258dp"
- android:layout_height="18dp"
- android:layout_marginStart="68dp"
- android:layout_marginTop="40dp"
- android:indeterminate="true"
- android:indeterminateOnly="true"
- android:visibility="gone"/>
+ <RelativeLayout
+ android:id="@+id/two_line_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginStart="52dp"
+ android:layout_marginEnd="69dp"
+ android:layout_marginTop="10dp">
+ <TextView
+ android:id="@+id/two_line_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="15dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="14sp"/>
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="15dp"
+ android:layout_marginBottom="7dp"
+ android:layout_alignParentBottom="true"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="12sp"
+ android:fontFamily="roboto-regular"
+ android:visibility="gone"/>
+ <SeekBar
+ android:id="@+id/volume_seekbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"/>
+ </RelativeLayout>
+
+ <ProgressBar
+ android:id="@+id/volume_indeterminate_progress"
+ style="@*android:style/Widget.Material.ProgressBar.Horizontal"
+ android:layout_width="258dp"
+ android:layout_height="18dp"
+ android:layout_marginStart="68dp"
+ android:layout_marginTop="40dp"
+ android:indeterminate="true"
+ android:indeterminateOnly="true"
+ android:visibility="gone"/>
+
+ <View
+ android:id="@+id/end_divider"
+ android:layout_width="1dp"
+ android:layout_height="36dp"
+ android:layout_marginEnd="68dp"
+ android:layout_gravity="right|center_vertical"
+ android:background="?android:attr/listDivider"
+ android:visibility="gone"/>
+
+ <ImageView
+ android:id="@+id/add_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="right|center_vertical"
+ android:layout_marginEnd="24dp"
+ android:src="@drawable/ic_add"
+ android:tint="?android:attr/colorAccent"
+ android:visibility="gone"/>
+
+ <CheckBox
+ android:id="@+id/check_box"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="right|center_vertical"
+ android:layout_marginEnd="24dp"
+ android:button="@drawable/ic_check_box"
+ android:visibility="gone"/>
+ </FrameLayout>
<View
- android:layout_width="1dp"
- android:layout_height="36dp"
- android:layout_marginEnd="68dp"
- android:layout_gravity="right|center_vertical"
+ android:id="@+id/bottom_divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_marginTop="12dp"
+ android:layout_marginBottom="12dp"
+ android:layout_gravity="bottom"
android:background="?android:attr/listDivider"
android:visibility="gone"/>
-
- <ImageView
- android:id="@+id/end_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="right|center_vertical"
- android:layout_marginEnd="24dp"
- android:visibility="gone"/>
-</FrameLayout>
\ No newline at end of file
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_space_widget_item.xml b/packages/SystemUI/res/layout/people_space_widget_item.xml
index a40bfff..e4de6f9 100644
--- a/packages/SystemUI/res/layout/people_space_widget_item.xml
+++ b/packages/SystemUI/res/layout/people_space_widget_item.xml
@@ -15,12 +15,12 @@
~ limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:background="@drawable/people_space_tile_view_card"
+ android:id="@+id/item"
android:orientation="vertical"
android:padding="6dp"
android:layout_marginBottom="6dp"
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 2bc6c90..68ef1a3 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -38,7 +38,7 @@
<string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"Uključi"</string>
<string name="battery_saver_start_action" msgid="4553256017945469937">"Uključi Uštedu baterije"</string>
<string name="status_bar_settings_settings_button" msgid="534331565185171556">"Podešavanja"</string>
- <string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"Wi-Fi"</string>
+ <string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"WiFi"</string>
<string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"Automatsko rotiranje ekrana"</string>
<string name="status_bar_settings_mute_label" msgid="914392730086057522">"UGASI"</string>
<string name="status_bar_settings_auto_brightness_label" msgid="2151934479226017725">"AUTOM."</string>
@@ -225,7 +225,7 @@
<string name="data_connection_cdma" msgid="7678457855627313518">"1X"</string>
<string name="data_connection_roaming" msgid="375650836665414797">"Roming"</string>
<string name="data_connection_edge" msgid="6316755666481405762">"EDGE"</string>
- <string name="accessibility_data_connection_wifi" msgid="4422160347472742434">"Wi-Fi"</string>
+ <string name="accessibility_data_connection_wifi" msgid="4422160347472742434">"WiFi"</string>
<string name="accessibility_no_sim" msgid="1140839832913084973">"Nema SIM kartice."</string>
<string name="accessibility_cell_data" msgid="172950885786007392">"Mobilni podaci"</string>
<string name="accessibility_cell_data_on" msgid="691666434519443162">"Mobilni podaci su uključeni"</string>
@@ -264,8 +264,8 @@
<string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Zaključan ekran za posao"</string>
<string name="accessibility_desc_close" msgid="8293708213442107755">"Zatvori"</string>
<string name="accessibility_quick_settings_wifi" msgid="167707325133803052">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
- <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi je isključen."</string>
- <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi je uključen."</string>
+ <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"WiFi je isključen."</string>
+ <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"WiFi je uključen."</string>
<string name="accessibility_quick_settings_mobile" msgid="1817825313718492906">"Mobilna mreža: <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string>
<string name="accessibility_quick_settings_battery" msgid="533594896310663853">"Baterija: <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_airplane_off" msgid="1275658769368793228">"Režim rada u avionu je isključen."</string>
@@ -374,19 +374,19 @@
<string name="quick_settings_user_label" msgid="1253515509432672496">"Ja"</string>
<string name="quick_settings_user_title" msgid="8673045967216204537">"Korisnik"</string>
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novi korisnik"</string>
- <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+ <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Veza nije uspostavljena"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nema mreže"</string>
- <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi je isključen"</string>
- <string name="quick_settings_wifi_on_label" msgid="2489928193654318511">"Wi-Fi je uključen"</string>
- <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"Nije dostupna nijedna Wi-Fi mreža"</string>
+ <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi je isključen"</string>
+ <string name="quick_settings_wifi_on_label" msgid="2489928193654318511">"WiFi je uključen"</string>
+ <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"Nije dostupna nijedna WiFi mreža"</string>
<string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"Uključuje se..."</string>
<string name="quick_settings_cast_title" msgid="2279220930629235211">"Prebacivanje ekrana"</string>
<string name="quick_settings_casting" msgid="1435880708719268055">"Prebacivanje"</string>
<string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"Neimenovani uređaj"</string>
<string name="quick_settings_cast_device_default_description" msgid="2580520859212250265">"Spremno za prebacivanje"</string>
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Nije dostupan nijedan uređaj"</string>
- <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi nije povezan"</string>
+ <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"WiFi nije povezan"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Osvetljenost"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="2325362583903258677">"AUTOMATSKA"</string>
<string name="quick_settings_inversion_label" msgid="5078769633069667698">"Obrni boje"</string>
@@ -642,8 +642,8 @@
<string name="output_none_found" msgid="5488087293120982770">"Nije pronađen nijedan uređaj"</string>
<string name="output_none_found_service_off" msgid="935667567681386368">"Nije pronađen nijedan uređaj. Probajte da uključite uslugu <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
<string name="output_service_bt" msgid="4315362133973911687">"Bluetooth"</string>
- <string name="output_service_wifi" msgid="9003667810868222134">"Wi-Fi"</string>
- <string name="output_service_bt_wifi" msgid="7186882540475524124">"Bluetooth i Wi-Fi"</string>
+ <string name="output_service_wifi" msgid="9003667810868222134">"WiFi"</string>
+ <string name="output_service_bt_wifi" msgid="7186882540475524124">"Bluetooth i WiFi"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Tjuner za korisnički interfejs sistema"</string>
<string name="show_battery_percentage" msgid="6235377891802910455">"Prikazuj ugrađeni procenat baterije"</string>
<string name="show_battery_percentage_summary" msgid="9053024758304102915">"Prikazivanje nivoa napunjenosti baterije u procentima unutar ikone na statusnoj traci kada se baterija ne puni"</string>
@@ -946,7 +946,7 @@
<string name="mobile_data" msgid="4564407557775397216">"Mobilni podaci"</string>
<string name="mobile_data_text_format" msgid="6806501540022589786">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="mobile_carrier_text_format" msgid="8912204177152950766">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>, <xliff:g id="MOBILE_DATA_TYPE">%2$s</xliff:g>"</string>
- <string name="wifi_is_off" msgid="5389597396308001471">"Wi-Fi je isključen"</string>
+ <string name="wifi_is_off" msgid="5389597396308001471">"WiFi je isključen"</string>
<string name="bt_is_off" msgid="7436344904889461591">"Bluetooth je isključen"</string>
<string name="dnd_is_off" msgid="3185706903793094463">"Režim Ne uznemiravaj je isključen"</string>
<string name="qs_dnd_prompt_auto_rule" msgid="3535469468310002616">"Automatsko pravilo (<xliff:g id="ID_1">%s</xliff:g>) je uključilo režim Ne uznemiravaj."</string>
@@ -958,7 +958,7 @@
<string name="running_foreground_services_title" msgid="5137313173431186685">"Aplikacije pokrenute u pozadini"</string>
<string name="running_foreground_services_msg" msgid="3009459259222695385">"Dodirnite za detalje o bateriji i potrošnji podataka"</string>
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Želite da isključite mobilne podatke?"</string>
- <string name="mobile_data_disable_message" msgid="8604966027899770415">"Nećete imati pristup podacima ili internetu preko mobilnog operatera <xliff:g id="CARRIER">%s</xliff:g>. Internet će biti dostupan samo preko Wi-Fi veze."</string>
+ <string name="mobile_data_disable_message" msgid="8604966027899770415">"Nećete imati pristup podacima ili internetu preko mobilnog operatera <xliff:g id="CARRIER">%s</xliff:g>. Internet će biti dostupan samo preko WiFi veze."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"mobilni operater"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Podešavanja ne mogu da verifikuju vaš odgovor jer aplikacija skriva zahtev za dozvolu."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Želite li da dozvolite aplikaciji <xliff:g id="APP_0">%1$s</xliff:g> da prikazuje isečke iz aplikacije <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 0e3f309..55e7d17 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Versuche noch einmal, den Screenshot zu erstellen"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Speichern des Screenshots aufgrund von zu wenig Speicher nicht möglich"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Die App oder deine Organisation lässt das Erstellen von Screenshots nicht zu"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"Screenshot bearbeiten"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Screenshot schließen"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshotvorschau"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Bildschirmaufzeichnung"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Empfehlungen werden geladen"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Medien"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Du kannst die aktuelle Sitzung ausblenden."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Aktuelle Sitzung kann nicht verborgen werden."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ablehnen"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Fortsetzen"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Einstellungen"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index fe38ea0..afa0759 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -605,7 +605,7 @@
<string name="screen_pinning_positive" msgid="3285785989665266984">"متوجه شدم"</string>
<string name="screen_pinning_negative" msgid="6882816864569211666">"نه متشکرم"</string>
<string name="screen_pinning_start" msgid="7483998671383371313">"برنامه پین شد"</string>
- <string name="screen_pinning_exit" msgid="4553787518387346893">"پین برنامه برداشته شد"</string>
+ <string name="screen_pinning_exit" msgid="4553787518387346893">"سنجاق از برنامه برداشته شد"</string>
<string name="quick_settings_reset_confirmation_title" msgid="463533331480997595">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> مخفی شود؟"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2320586180785674186">"دفعه بعد که آن را روشن کنید، در تنظیمات نشان داده میشود."</string>
<string name="quick_settings_reset_confirmation_button" msgid="3341477479055016776">"پنهان کردن"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index acaa442..02c72db 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ફરીથી સ્ક્રીનશૉટ લેવાનો પ્રયાસ કરો"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"મર્યાદિત સ્ટોરેજ સ્પેસને કારણે સ્ક્રીનશૉટ સાચવી શકાતો નથી"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ઍપ્લિકેશન કે તમારી સંસ્થા દ્વારા સ્ક્રીનશૉટ લેવાની મંજૂરી નથી"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"સ્ક્રીનશૉટમાં ફેરફાર કરો"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"સ્ક્રીનશૉટ છોડી દો"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"સ્ક્રીનશૉટનો પ્રીવ્યૂ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"સ્ક્રીન રેકૉર્ડર"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"સુઝાવ લોડ કરી રહ્યાં છીએ"</string>
<string name="controls_media_title" msgid="1746947284862928133">"મીડિયા"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"હાલનું સત્ર છુપાવો."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"વર્તમાન સત્ર છુપાવી શકાતું નથી."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"છોડી દો"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ફરી શરૂ કરો"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"સેટિંગ"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 9f0f0ae..1cc1b4e 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -1064,7 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"ກຳລັງໂຫຼດຄຳແນະນຳ"</string>
<string name="controls_media_title" msgid="1746947284862928133">"ມີເດຍ"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"ເຊື່ອງເຊດຊັນປັດຈຸບັນ."</string>
- <string name="controls_media_active_session" msgid="1984383994625845642">"ບໍ່ສາມາດເຊື່ອເຊດຊັນປັດຈຸບັນໄດ້."</string>
+ <string name="controls_media_active_session" msgid="1984383994625845642">"ບໍ່ສາມາດເຊື່ອງເຊດຊັນປັດຈຸບັນໄດ້."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"ປິດໄວ້"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ສືບຕໍ່"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ການຕັ້ງຄ່າ"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 5b07882..57c1e07 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रीनशॉट पुन्हा घेण्याचा प्रयत्न करा"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"मर्यादित स्टोरेज जागेमुळे स्क्रीनशॉट सेव्ह करू शकत नाही"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"अॅप किंवा आपल्या संस्थेद्वारे स्क्रीनशॉट घेण्याची अनुमती नाही"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"स्क्रीनशॉट संपादित करा"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"स्क्रीनशॉट डिसमिस करा"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रीनशॉटचे पूर्वावलोकन"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रेकॉर्डर"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"शिफारशी लोड करत आहे"</string>
<string name="controls_media_title" msgid="1746947284862928133">"मीडिया"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"सध्याचे सेशन लपवा."</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"सध्याचे सेशन लपवता येणार नाही."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"डिसमिस करा"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"पुन्हा सुरू करा"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिंग्ज"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 5eea7fd..c3c8903 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रिनसट फेरि लिएर हेर्नुहोस्"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"भण्डारण ठाउँ सीमित भएका कारण स्क्रिनसट सुरक्षित गर्न सकिएन"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"उक्त एप वा तपाईंको संगठनले स्क्रिनसटहरू लिन दिँदैन"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"स्क्रिनसट सम्पादन गर्नुहोस्"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"स्क्रिनसट हटाउनुहोस्"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रिनसटको पूर्वावलोकन"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"स्क्रिन रेकर्डर"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"सिफारिसहरू लोड गर्दै"</string>
<string name="controls_media_title" msgid="1746947284862928133">"मिडिया"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"हालको सत्र लुकाउनुहोस्।"</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"हाल चलिरहेको सत्र लुकाउन सकिँदैन।"</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"हटाउनुहोस्"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"सुचारु गर्नुहोस्"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिङ"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index cd95885..1ad7a91 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -38,7 +38,7 @@
<string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"Укључи"</string>
<string name="battery_saver_start_action" msgid="4553256017945469937">"Укључи Уштеду батерије"</string>
<string name="status_bar_settings_settings_button" msgid="534331565185171556">"Подешавања"</string>
- <string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"Wi-Fi"</string>
+ <string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"WiFi"</string>
<string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"Аутоматско ротирање екрана"</string>
<string name="status_bar_settings_mute_label" msgid="914392730086057522">"УГАСИ"</string>
<string name="status_bar_settings_auto_brightness_label" msgid="2151934479226017725">"АУТОM."</string>
@@ -225,7 +225,7 @@
<string name="data_connection_cdma" msgid="7678457855627313518">"1X"</string>
<string name="data_connection_roaming" msgid="375650836665414797">"Роминг"</string>
<string name="data_connection_edge" msgid="6316755666481405762">"EDGE"</string>
- <string name="accessibility_data_connection_wifi" msgid="4422160347472742434">"Wi-Fi"</string>
+ <string name="accessibility_data_connection_wifi" msgid="4422160347472742434">"WiFi"</string>
<string name="accessibility_no_sim" msgid="1140839832913084973">"Нема SIM картице."</string>
<string name="accessibility_cell_data" msgid="172950885786007392">"Мобилни подаци"</string>
<string name="accessibility_cell_data_on" msgid="691666434519443162">"Мобилни подаци су укључени"</string>
@@ -264,8 +264,8 @@
<string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Закључан екран за посао"</string>
<string name="accessibility_desc_close" msgid="8293708213442107755">"Затвори"</string>
<string name="accessibility_quick_settings_wifi" msgid="167707325133803052">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
- <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wi-Fi је искључен."</string>
- <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wi-Fi је укључен."</string>
+ <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"WiFi је искључен."</string>
+ <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"WiFi је укључен."</string>
<string name="accessibility_quick_settings_mobile" msgid="1817825313718492906">"Мобилна мрежа: <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string>
<string name="accessibility_quick_settings_battery" msgid="533594896310663853">"Батерија: <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_airplane_off" msgid="1275658769368793228">"Режим рада у авиону је искључен."</string>
@@ -374,19 +374,19 @@
<string name="quick_settings_user_label" msgid="1253515509432672496">"Ја"</string>
<string name="quick_settings_user_title" msgid="8673045967216204537">"Корисник"</string>
<string name="quick_settings_user_new_user" msgid="3347905871336069666">"Нови корисник"</string>
- <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+ <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Веза није успостављена"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Нема мреже"</string>
- <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi је искључен"</string>
- <string name="quick_settings_wifi_on_label" msgid="2489928193654318511">"Wi-Fi је укључен"</string>
- <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"Није доступна ниједна Wi-Fi мрежа"</string>
+ <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi је искључен"</string>
+ <string name="quick_settings_wifi_on_label" msgid="2489928193654318511">"WiFi је укључен"</string>
+ <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"Није доступна ниједна WiFi мрежа"</string>
<string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"Укључује се..."</string>
<string name="quick_settings_cast_title" msgid="2279220930629235211">"Пребацивање екрана"</string>
<string name="quick_settings_casting" msgid="1435880708719268055">"Пребацивање"</string>
<string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"Неименовани уређај"</string>
<string name="quick_settings_cast_device_default_description" msgid="2580520859212250265">"Спремно за пребацивање"</string>
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"Није доступан ниједан уређај"</string>
- <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi није повезан"</string>
+ <string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"WiFi није повезан"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Осветљеност"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="2325362583903258677">"АУТОМАТСКА"</string>
<string name="quick_settings_inversion_label" msgid="5078769633069667698">"Обрни боје"</string>
@@ -642,8 +642,8 @@
<string name="output_none_found" msgid="5488087293120982770">"Није пронађен ниједан уређај"</string>
<string name="output_none_found_service_off" msgid="935667567681386368">"Није пронађен ниједан уређај. Пробајте да укључите услугу <xliff:g id="SERVICE">%1$s</xliff:g>"</string>
<string name="output_service_bt" msgid="4315362133973911687">"Bluetooth"</string>
- <string name="output_service_wifi" msgid="9003667810868222134">"Wi-Fi"</string>
- <string name="output_service_bt_wifi" msgid="7186882540475524124">"Bluetooth и Wi-Fi"</string>
+ <string name="output_service_wifi" msgid="9003667810868222134">"WiFi"</string>
+ <string name="output_service_bt_wifi" msgid="7186882540475524124">"Bluetooth и WiFi"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Тјунер за кориснички интерфејс система"</string>
<string name="show_battery_percentage" msgid="6235377891802910455">"Приказуј уграђени проценат батерије"</string>
<string name="show_battery_percentage_summary" msgid="9053024758304102915">"Приказивање нивоа напуњености батерије у процентима унутар иконе на статусној траци када се батерија не пуни"</string>
@@ -946,7 +946,7 @@
<string name="mobile_data" msgid="4564407557775397216">"Мобилни подаци"</string>
<string name="mobile_data_text_format" msgid="6806501540022589786">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="mobile_carrier_text_format" msgid="8912204177152950766">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>, <xliff:g id="MOBILE_DATA_TYPE">%2$s</xliff:g>"</string>
- <string name="wifi_is_off" msgid="5389597396308001471">"Wi-Fi је искључен"</string>
+ <string name="wifi_is_off" msgid="5389597396308001471">"WiFi је искључен"</string>
<string name="bt_is_off" msgid="7436344904889461591">"Bluetooth је искључен"</string>
<string name="dnd_is_off" msgid="3185706903793094463">"Режим Не узнемиравај је искључен"</string>
<string name="qs_dnd_prompt_auto_rule" msgid="3535469468310002616">"Аутоматско правило (<xliff:g id="ID_1">%s</xliff:g>) је укључило режим Не узнемиравај."</string>
@@ -958,7 +958,7 @@
<string name="running_foreground_services_title" msgid="5137313173431186685">"Апликације покренуте у позадини"</string>
<string name="running_foreground_services_msg" msgid="3009459259222695385">"Додирните за детаље о батерији и потрошњи података"</string>
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Желите да искључите мобилне податке?"</string>
- <string name="mobile_data_disable_message" msgid="8604966027899770415">"Нећете имати приступ подацима или интернету преко мобилног оператера <xliff:g id="CARRIER">%s</xliff:g>. Интернет ће бити доступан само преко Wi-Fi везе."</string>
+ <string name="mobile_data_disable_message" msgid="8604966027899770415">"Нећете имати приступ подацима или интернету преко мобилног оператера <xliff:g id="CARRIER">%s</xliff:g>. Интернет ће бити доступан само преко WiFi везе."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"мобилни оператер"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Подешавања не могу да верификују ваш одговор јер апликација скрива захтев за дозволу."</string>
<string name="slice_permission_title" msgid="3262615140094151017">"Желите ли да дозволите апликацији <xliff:g id="APP_0">%1$s</xliff:g> да приказује исечке из апликације <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 1bfa853..4c0ebaa 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -86,8 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"دوبارہ اسکرین شاٹ لینے کی کوشش کریں"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"اسٹوریج کی محدود جگہ کی وجہ سے اسکرین شاٹ کو محفوظ نہیں کیا جا سکتا"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ایپ یا آپ کی تنظیم کی جانب سے اسکرین شاٹس لینے کی اجازت نہیں ہے"</string>
- <!-- no translation found for screenshot_edit (3510496440489019191) -->
- <skip />
+ <string name="screenshot_edit" msgid="3510496440489019191">"اسکرین شاٹ میں ترمیم کریں"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"اسکرین شاٹ برخاست کریں"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"اسکرین شاٹ کا پیش منظر"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"سکرین ریکارڈر"</string>
@@ -1065,8 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"تجاویز لوڈ ہو رہی ہیں"</string>
<string name="controls_media_title" msgid="1746947284862928133">"میڈیا"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"موجودہ سیشن چھپائیں۔"</string>
- <!-- no translation found for controls_media_active_session (1984383994625845642) -->
- <skip />
+ <string name="controls_media_active_session" msgid="1984383994625845642">"موجودہ سیشن کو چھپایا نہیں جا سکتا۔"</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"برخاست کریں"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"دوبارہ شروع کریں"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ترتیبات"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 332a26d..33d4f4c 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -1064,7 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Tavsiyalar yuklanmoqda"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Joriy seans berkitilsin."</string>
- <string name="controls_media_active_session" msgid="1984383994625845642">"Joriy seans berkilmaydi."</string>
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Joriy seansni berkitish imkonsiz."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Yopish"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Davom etish"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Sozlamalar"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index d63724d..57ce0b5 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -387,9 +387,6 @@
off-screen. -->
<bool name="config_fadeDependingOnAmountSwiped">false</bool>
- <!-- Whether or not to show the expand button at the end of the notification header. -->
- <bool name="config_showNotificationExpandButtonAtEnd">false</bool>
-
<!-- Whether or the notifications should be clipped to be reduced in height if it has been
scrolled to the top of the screen. -->
<bool name="config_clipNotificationScrollToTop">true</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 4b1ed0a..1ab776b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1218,6 +1218,8 @@
<dimen name="bubble_message_padding">4dp</dimen>
<!-- Offset between bubbles in their stacked position. -->
<dimen name="bubble_stack_offset">10dp</dimen>
+ <!-- Offset between stack y and animation y for bubble swap. -->
+ <dimen name="bubble_swap_animation_offset">15dp</dimen>
<!-- How far offscreen the bubble stack rests. Cuts off padding and part of icon bitmap. -->
<dimen name="bubble_stack_offscreen">9dp</dimen>
<!-- How far down the screen the stack starts. -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
new file mode 100644
index 0000000..22ffd28
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
@@ -0,0 +1,74 @@
+/*
+ * 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.shared.pip;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.SurfaceControl;
+
+/**
+ * TODO(b/171721389): unify this class with
+ * {@link com.android.wm.shell.pip.PipSurfaceTransactionHelper}, for instance, there should be one
+ * source of truth on enabling/disabling and the actual value of corner radius.
+ */
+public class PipSurfaceTransactionHelper {
+ private final Matrix mTmpTransform = new Matrix();
+ private final float[] mTmpFloat9 = new float[9];
+ private final RectF mTmpSourceRectF = new RectF();
+ private final Rect mTmpDestinationRect = new Rect();
+
+ public void scaleAndCrop(SurfaceControl.Transaction tx, SurfaceControl leash,
+ Rect sourceBounds, Rect destinationBounds, Rect insets) {
+ mTmpSourceRectF.set(sourceBounds);
+ mTmpDestinationRect.set(sourceBounds);
+ mTmpDestinationRect.inset(insets);
+ // Scale by the shortest edge and offset such that the top/left of the scaled inset
+ // source rect aligns with the top/left of the destination bounds
+ final float scale = sourceBounds.width() <= sourceBounds.height()
+ ? (float) destinationBounds.width() / sourceBounds.width()
+ : (float) destinationBounds.height() / sourceBounds.height();
+ final float left = destinationBounds.left - insets.left * scale;
+ final float top = destinationBounds.top - insets.top * scale;
+ mTmpTransform.setScale(scale, scale);
+ tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
+ .setWindowCrop(leash, mTmpDestinationRect)
+ .setPosition(leash, left, top);
+ }
+
+ public void reset(SurfaceControl.Transaction tx, SurfaceControl leash, Rect destinationBounds) {
+ resetScale(tx, leash, destinationBounds);
+ resetCornerRadius(tx, leash);
+ crop(tx, leash, destinationBounds);
+ }
+
+ public void resetScale(SurfaceControl.Transaction tx, SurfaceControl leash,
+ Rect destinationBounds) {
+ tx.setMatrix(leash, Matrix.IDENTITY_MATRIX, mTmpFloat9)
+ .setPosition(leash, destinationBounds.left, destinationBounds.top);
+ }
+
+ public void resetCornerRadius(SurfaceControl.Transaction tx, SurfaceControl leash) {
+ tx.setCornerRadius(leash, 0);
+ }
+
+ public void crop(SurfaceControl.Transaction tx, SurfaceControl leash,
+ Rect destinationBounds) {
+ tx.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height())
+ .setPosition(leash, destinationBounds.left, destinationBounds.top);
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ClipDescriptionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ClipDescriptionCompat.java
new file mode 100644
index 0000000..0b1141e
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ClipDescriptionCompat.java
@@ -0,0 +1,39 @@
+/*
+ * 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.shared.system;
+
+import android.content.ClipDescription;
+import android.content.Intent;
+
+/**
+ * Wrapper around ClipDescription.
+ */
+public abstract class ClipDescriptionCompat {
+
+ public static String MIMETYPE_APPLICATION_ACTIVITY =
+ ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
+
+ public static String MIMETYPE_APPLICATION_SHORTCUT =
+ ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
+
+ public static String MIMETYPE_APPLICATION_TASK =
+ ClipDescription.MIMETYPE_APPLICATION_TASK;
+
+ public static String EXTRA_PENDING_INTENT = ClipDescription.EXTRA_PENDING_INTENT;
+
+ public static String EXTRA_TASK_ID = Intent.EXTRA_TASK_ID;
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LauncherAppsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LauncherAppsCompat.java
new file mode 100644
index 0000000..d24c779
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LauncherAppsCompat.java
@@ -0,0 +1,34 @@
+/*
+ * 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.shared.system;
+
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.pm.LauncherApps;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+/**
+ * Wrapper around LauncherApps.
+ */
+public abstract class LauncherAppsCompat {
+
+ public static PendingIntent getMainActivityLaunchIntent(LauncherApps launcherApps,
+ ComponentName component, Bundle startActivityOptions, UserHandle user) {
+ return launcherApps.getMainActivityLaunchIntent(component, startActivityOptions, user);
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 76513c6..611c4b7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -17,6 +17,7 @@
package com.android.systemui.shared.system;
import android.app.ActivityManager.TaskSnapshot;
+import android.graphics.Rect;
import android.os.RemoteException;
import android.util.Log;
import android.view.IRecentsAnimationController;
@@ -70,6 +71,21 @@
}
/**
+ * Sets the final bounds on a Task. This is used by Launcher to notify the system that
+ * animating Activity to PiP has completed and the associated task surface should be updated
+ * accordingly. This should be called before `finish`
+ * @param taskId Task id of the Activity in PiP mode.
+ * @param destinationBounds Bounds of the PiP window on home.
+ */
+ public void setFinishTaskBounds(int taskId, Rect destinationBounds) {
+ try {
+ mAnimationController.setFinishTaskBounds(taskId, destinationBounds);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Failed to set finish task bounds", e);
+ }
+ }
+
+ /**
* Finish the current recents animation.
* @param toHome Going to home or back to the previous app.
* @param sendUserLeaveHint determines whether userLeaveHint will be set true to the previous
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java
index 326c2aa..7b9ebc0 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java
@@ -17,8 +17,11 @@
package com.android.systemui.shared.system;
import android.app.ActivityManager;
+import android.app.PictureInPictureParams;
import android.app.TaskInfo;
import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
public class TaskInfoCompat {
@@ -34,6 +37,10 @@
return info.configuration.windowConfiguration.getWindowingMode();
}
+ public static Rect getWindowConfigurationBounds(TaskInfo info) {
+ return info.configuration.windowConfiguration.getBounds();
+ }
+
public static boolean supportsSplitScreenMultiWindow(TaskInfo info) {
return info.supportsSplitScreenMultiWindow;
}
@@ -45,4 +52,16 @@
public static ActivityManager.TaskDescription getTaskDescription(TaskInfo info) {
return info.taskDescription;
}
+
+ public static ActivityInfo getTopActivityInfo(TaskInfo info) {
+ return info.topActivityInfo;
+ }
+
+ public static boolean isAutoEnterPipEnabled(PictureInPictureParams params) {
+ return params.isAutoEnterEnabled();
+ }
+
+ public static Rect getPipSourceRectHint(PictureInPictureParams params) {
+ return params.getSourceRectHint();
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 5b41d5f..d3066b4 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -39,27 +39,29 @@
private static final String TAG = "WindowManagerWrapper";
- public static final int TRANSIT_UNSET = WindowManager.TRANSIT_UNSET;
- public static final int TRANSIT_NONE = WindowManager.TRANSIT_NONE;
- public static final int TRANSIT_ACTIVITY_OPEN = WindowManager.TRANSIT_ACTIVITY_OPEN;
- public static final int TRANSIT_ACTIVITY_CLOSE = WindowManager.TRANSIT_ACTIVITY_CLOSE;
- public static final int TRANSIT_TASK_OPEN = WindowManager.TRANSIT_TASK_OPEN;
- public static final int TRANSIT_TASK_CLOSE = WindowManager.TRANSIT_TASK_CLOSE;
- public static final int TRANSIT_TASK_TO_FRONT = WindowManager.TRANSIT_TASK_TO_FRONT;
- public static final int TRANSIT_TASK_TO_BACK = WindowManager.TRANSIT_TASK_TO_BACK;
- public static final int TRANSIT_WALLPAPER_CLOSE = WindowManager.TRANSIT_WALLPAPER_CLOSE;
- public static final int TRANSIT_WALLPAPER_OPEN = WindowManager.TRANSIT_WALLPAPER_OPEN;
+ public static final int TRANSIT_UNSET = WindowManager.TRANSIT_OLD_UNSET;
+ public static final int TRANSIT_NONE = WindowManager.TRANSIT_OLD_NONE;
+ public static final int TRANSIT_ACTIVITY_OPEN = WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+ public static final int TRANSIT_ACTIVITY_CLOSE = WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
+ public static final int TRANSIT_TASK_OPEN = WindowManager.TRANSIT_OLD_TASK_OPEN;
+ public static final int TRANSIT_TASK_CLOSE = WindowManager.TRANSIT_OLD_TASK_CLOSE;
+ public static final int TRANSIT_TASK_TO_FRONT = WindowManager.TRANSIT_OLD_TASK_TO_FRONT;
+ public static final int TRANSIT_TASK_TO_BACK = WindowManager.TRANSIT_OLD_TASK_TO_BACK;
+ public static final int TRANSIT_WALLPAPER_CLOSE = WindowManager.TRANSIT_OLD_WALLPAPER_CLOSE;
+ public static final int TRANSIT_WALLPAPER_OPEN = WindowManager.TRANSIT_OLD_WALLPAPER_OPEN;
public static final int TRANSIT_WALLPAPER_INTRA_OPEN =
- WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN;
+ WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
public static final int TRANSIT_WALLPAPER_INTRA_CLOSE =
- WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
- public static final int TRANSIT_TASK_OPEN_BEHIND = WindowManager.TRANSIT_TASK_OPEN_BEHIND;
- public static final int TRANSIT_ACTIVITY_RELAUNCH = WindowManager.TRANSIT_ACTIVITY_RELAUNCH;
- public static final int TRANSIT_KEYGUARD_GOING_AWAY = WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+ WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
+ public static final int TRANSIT_TASK_OPEN_BEHIND = WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
+ public static final int TRANSIT_ACTIVITY_RELAUNCH = WindowManager.TRANSIT_OLD_ACTIVITY_RELAUNCH;
+ public static final int TRANSIT_KEYGUARD_GOING_AWAY =
+ WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
public static final int TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER =
- WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
- public static final int TRANSIT_KEYGUARD_OCCLUDE = WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
- public static final int TRANSIT_KEYGUARD_UNOCCLUDE = WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+ WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+ public static final int TRANSIT_KEYGUARD_OCCLUDE = WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
+ public static final int TRANSIT_KEYGUARD_UNOCCLUDE =
+ WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
public static final int NAV_BAR_POS_INVALID = NAV_BAR_INVALID;
public static final int NAV_BAR_POS_LEFT = NAV_BAR_LEFT;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 53f8474..217cf70 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -81,8 +81,7 @@
abstract void resetState();
@Override
- public void init() {
- super.init();
+ public void onInit() {
mMessageAreaController.init();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 1562444..628193d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -20,6 +20,7 @@
import android.content.res.Resources;
import android.text.format.DateFormat;
import android.util.TypedValue;
+import android.view.View;
import android.view.ViewGroup;
import com.android.internal.colorextraction.ColorExtractor;
@@ -29,6 +30,8 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.NotificationIconAreaController;
+import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.util.ViewController;
import java.util.Locale;
@@ -47,6 +50,9 @@
private final SysuiColorExtractor mColorExtractor;
private final ClockManager mClockManager;
private final KeyguardSliceViewController mKeyguardSliceViewController;
+ private final NotificationIconAreaController mNotificationIconAreaController;
+
+ private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
private final StatusBarStateController.StateListener mStateListener =
new StatusBarStateController.StateListener() {
@@ -79,21 +85,22 @@
@Main Resources resources,
StatusBarStateController statusBarStateController,
SysuiColorExtractor colorExtractor, ClockManager clockManager,
- KeyguardSliceViewController keyguardSliceViewController) {
+ KeyguardSliceViewController keyguardSliceViewController,
+ NotificationIconAreaController notificationIconAreaController) {
super(keyguardClockSwitch);
mResources = resources;
mStatusBarStateController = statusBarStateController;
mColorExtractor = colorExtractor;
mClockManager = clockManager;
mKeyguardSliceViewController = keyguardSliceViewController;
+ mNotificationIconAreaController = notificationIconAreaController;
}
/**
* Attach the controller to the view it relates to.
*/
@Override
- public void init() {
- super.init();
+ public void onInit() {
mKeyguardSliceViewController.init();
}
@@ -106,6 +113,7 @@
mStatusBarStateController.addCallback(mStateListener);
mColorExtractor.addOnColorsChangedListener(mColorsListener);
mView.updateColors(getGradientColors());
+ updateAodIcons();
}
@Override
@@ -175,7 +183,9 @@
* Update lockscreen mode that may change clock display.
*/
void updateLockScreenMode(int mode) {
- mView.updateLockScreenMode(mode);
+ mLockScreenMode = mode;
+ mView.updateLockScreenMode(mLockScreenMode);
+ updateAodIcons();
}
void updateTimeZone(TimeZone timeZone) {
@@ -188,6 +198,19 @@
mView.setFormat24Hour(Patterns.sClockView24);
}
+ private void updateAodIcons() {
+ NotificationIconContainer nic = (NotificationIconContainer)
+ mView.findViewById(
+ com.android.systemui.R.id.left_aligned_notification_icon_container);
+
+ if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
+ // alt icon area is set in KeyguardClockSwitchController
+ mNotificationIconAreaController.setupAodIcons(nic, mLockScreenMode);
+ } else {
+ nic.setVisibility(View.GONE);
+ }
+ }
+
private void setClockPlugin(ClockPlugin plugin) {
mView.setClockPlugin(plugin, mStatusBarStateController.getState());
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index 351369c..3fafa5c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -178,8 +178,7 @@
}
/** Initialize the Controller. */
- public void init() {
- super.init();
+ public void onInit() {
mKeyguardSecurityContainerController.init();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 3db9db7..730c177 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -190,8 +190,8 @@
}
@Override
- public void init() {
- super.init();
+ public void onInit() {
+ super.onInit();
mMessageAreaController.init();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 1c23605..9a51150 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -169,8 +169,7 @@
}
@Override
- public void init() {
- super.init();
+ public void onInit() {
mSecurityViewFlipperController.init();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 7705db4..cc0d1b6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -27,6 +27,8 @@
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.phone.NotificationIconAreaController;
+import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;
@@ -50,8 +52,10 @@
private final KeyguardStateController mKeyguardStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ConfigurationController mConfigurationController;
+ private final NotificationIconAreaController mNotificationIconAreaController;
private boolean mKeyguardStatusViewAnimating;
+ private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
@Inject
public KeyguardStatusViewController(
@@ -60,18 +64,19 @@
KeyguardClockSwitchController keyguardClockSwitchController,
KeyguardStateController keyguardStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ NotificationIconAreaController notificationIconAreaController) {
super(keyguardStatusView);
mKeyguardSliceViewController = keyguardSliceViewController;
mKeyguardClockSwitchController = keyguardClockSwitchController;
mKeyguardStateController = keyguardStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mConfigurationController = configurationController;
+ mNotificationIconAreaController = notificationIconAreaController;
}
@Override
- public void init() {
- super.init();
+ public void onInit() {
mKeyguardClockSwitchController.init();
}
@@ -79,6 +84,7 @@
protected void onViewAttached() {
mKeyguardUpdateMonitor.registerCallback(mInfoCallback);
mConfigurationController.addCallback(mConfigurationListener);
+ updateAodIcons();
}
@Override
@@ -249,6 +255,17 @@
mKeyguardClockSwitchController.refresh();
}
+ private void updateAodIcons() {
+ NotificationIconContainer nic = (NotificationIconContainer)
+ mView.findViewById(com.android.systemui.R.id.clock_notification_icon_container);
+ if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
+ // alternate icon area is set in KeyguardClockSwitchController
+ mNotificationIconAreaController.setupAodIcons(nic, mLockScreenMode);
+ } else {
+ nic.setVisibility(View.GONE);
+ }
+ }
+
private final ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@Override
@@ -266,8 +283,10 @@
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onLockScreenModeChanged(int mode) {
+ mLockScreenMode = mode;
mKeyguardClockSwitchController.updateLockScreenMode(mode);
mKeyguardSliceViewController.updateLockScreenMode(mode);
+ updateAodIcons();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 3348bd1..e709830 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -113,7 +113,7 @@
// TODO(b/170396074): Remove this when we don't need an icon anymore.
try {
int showPeopleSpace = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.SHOW_PEOPLE_SPACE);
+ Settings.Global.SHOW_PEOPLE_SPACE, 0);
context.getPackageManager().setComponentEnabledSetting(
new ComponentName(context, PeopleSpaceActivity.class),
showPeopleSpace == 1
@@ -128,7 +128,7 @@
// TODO(b/170396074): Remove this when we don't need a widget anymore.
try {
int showPeopleSpace = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.SHOW_PEOPLE_SPACE);
+ Settings.Global.SHOW_PEOPLE_SPACE, 0);
context.getPackageManager().setComponentEnabledSetting(
new ComponentName(context, PeopleSpaceWidgetProvider.class),
showPeopleSpace == 1
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index bdd0b55..187c31f 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -103,11 +103,13 @@
// components that shouldn't be run in the test environment
builder = builder.setPip(mWMComponent.getPip())
.setSplitScreen(mWMComponent.getSplitScreen())
- .setOneHanded(mWMComponent.getOneHanded());
+ .setOneHanded(mWMComponent.getOneHanded())
+ .setShellDump(mWMComponent.getShellDump());
} else {
builder = builder.setPip(Optional.ofNullable(null))
.setSplitScreen(Optional.ofNullable(null))
- .setOneHanded(Optional.ofNullable(null));
+ .setOneHanded(Optional.ofNullable(null))
+ .setShellDump(Optional.ofNullable(null));
}
mSysUIComponent = builder
.setInputConsumerController(mWMComponent.getInputConsumerController())
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index c6e5f09..64a2aca 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -24,6 +24,7 @@
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.os.Bundle;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.MathUtils;
import android.view.Gravity;
@@ -232,8 +233,11 @@
mMagnificationMode ^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
mMagnificationMode = newMode;
mImageView.setImageResource(getIconResId(newMode));
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, newMode);
+ Settings.Secure.putIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
+ newMode,
+ UserHandle.USER_CURRENT);
}
private void handleSingleTap() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index a677e91..874c73b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -472,6 +472,15 @@
}
}
+ /**
+ * Whether the current user has a UDFP enrolled.
+ */
+ public boolean hasUdfpsEnrolled() {
+ // TODO: (b/171392825) right now only checks whether the UDFPS sensor exists on this device
+ // but not whether user has enrolled or not
+ return mUdfpsController != null;
+ }
+
private void showDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) {
mCurrentDialogArgs = args;
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index 83de324..eea168a 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -112,7 +112,7 @@
* @param executor An executor to dispatch [BroadcastReceiver.onReceive]. Pass null to use an
* executor in the main thread (default).
* @param user A user handle to determine which broadcast should be dispatched to this receiver.
- * By default, it is the user of the context (system user in SystemUI).
+ * Pass `null` to use the user of the context (system user in SystemUI).
* @throws IllegalArgumentException if the filter has other constraints that are not actions or
* categories or the filter has no actions.
*/
@@ -120,13 +120,17 @@
open fun registerReceiver(
receiver: BroadcastReceiver,
filter: IntentFilter,
- executor: Executor? = context.mainExecutor,
- user: UserHandle = context.user
+ executor: Executor? = null,
+ user: UserHandle? = null
) {
checkFilter(filter)
this.handler
- .obtainMessage(MSG_ADD_RECEIVER,
- ReceiverData(receiver, filter, executor ?: context.mainExecutor, user))
+ .obtainMessage(MSG_ADD_RECEIVER, ReceiverData(
+ receiver,
+ filter,
+ executor ?: context.mainExecutor,
+ user ?: context.user
+ ))
.sendToTarget()
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
index 9f7358b..8bcffc8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
@@ -113,6 +113,18 @@
setClickable(true);
}
+ public void showDotAndBadge(boolean onLeft) {
+ removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.BEHIND_STACK);
+ animateDotBadgePositions(onLeft);
+
+ }
+
+ public void hideDotAndBadge(boolean onLeft) {
+ addDotSuppressionFlag(BadgedImageView.SuppressionFlag.BEHIND_STACK);
+ mOnLeft = onLeft;
+ hideBadge();
+ }
+
/**
* Updates the view with provided info.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index bc06020..cf2e133 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -252,8 +252,7 @@
mPositioner = mBubbles.getPositioner();
- mTaskView = new TaskView(mContext, mBubbles.getTaskOrganizer(),
- new HandlerExecutor(getHandler()));
+ mTaskView = new TaskView(mContext, mBubbles.getTaskOrganizer());
// Set ActivityView's alpha value as zero, since there is no view content to be shown.
setContentVisibility(false);
@@ -310,6 +309,12 @@
setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
}
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mTaskView.setExecutor(new HandlerExecutor(getHandler()));
+ }
+
void updateDimensions() {
Resources res = getResources();
mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 0714c5e..1201d42 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -90,6 +90,7 @@
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
/**
* Renders bubbles in a stack and handles animating expanded and collapsed states.
@@ -1479,12 +1480,24 @@
logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
}
+ /**
+ * Update bubble order and pointer position.
+ */
public void updateBubbleOrder(List<Bubble> bubbles) {
- for (int i = 0; i < bubbles.size(); i++) {
- Bubble bubble = bubbles.get(i);
- mBubbleContainer.reorderView(bubble.getIconView(), i);
+ final Runnable reorder = () -> {
+ for (int i = 0; i < bubbles.size(); i++) {
+ Bubble bubble = bubbles.get(i);
+ mBubbleContainer.reorderView(bubble.getIconView(), i);
+ }
+ };
+ if (mIsExpanded) {
+ reorder.run();
+ updateBubbleIcons();
+ } else {
+ List<View> bubbleViews = bubbles.stream()
+ .map(b -> b.getIconView()).collect(Collectors.toList());
+ mStackAnimationController.animateReorder(bubbleViews, reorder);
}
- updateBubbleIcons();
updatePointerPosition();
}
@@ -2595,17 +2608,11 @@
bv.setZ((mMaxBubbles * mBubbleElevation) - i);
if (mIsExpanded) {
- bv.removeDotSuppressionFlag(
- BadgedImageView.SuppressionFlag.BEHIND_STACK);
- bv.animateDotBadgePositions(false /* onLeft */);
+ bv.showDotAndBadge(false /* onLeft */);
} else if (i == 0) {
- bv.removeDotSuppressionFlag(
- BadgedImageView.SuppressionFlag.BEHIND_STACK);
- bv.animateDotBadgePositions(!mStackOnLeftOrWillBe);
+ bv.showDotAndBadge(!mStackOnLeftOrWillBe);
} else {
- bv.addDotSuppressionFlag(
- BadgedImageView.SuppressionFlag.BEHIND_STACK);
- bv.hideBadge();
+ bv.hideDotAndBadge(!mStackOnLeftOrWillBe);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java b/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java
index 85616d1..0a2cfbf 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java
@@ -30,7 +30,6 @@
import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
import android.os.Binder;
-import android.os.Handler;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
@@ -82,21 +81,25 @@
private boolean mSurfaceCreated;
private boolean mIsInitialized;
private Listener mListener;
- private final Executor mExecutor;
+ private Executor mExecutor;
private final Rect mTmpRect = new Rect();
private final Rect mTmpRootRect = new Rect();
- public TaskView(Context context, ShellTaskOrganizer organizer, Executor executor) {
+ public TaskView(Context context, ShellTaskOrganizer organizer) {
super(context, null, 0, 0, true /* disableBackgroundLayer */);
- mExecutor = executor;
mTaskOrganizer = organizer;
setUseAlpha();
getHolder().addCallback(this);
mGuard.open("release");
}
+ // TODO: Use TaskOrganizer executor when part of wmshell proper
+ public void setExecutor(Executor executor) {
+ mExecutor = executor;
+ }
+
/**
* Only one listener may be set on the view, throws an exception otherwise.
*/
@@ -225,6 +228,7 @@
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl leash) {
+ if (mExecutor == null) return;
mExecutor.execute(() -> {
mTaskInfo = taskInfo;
mTaskToken = taskInfo.token;
@@ -253,6 +257,7 @@
@Override
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mExecutor == null) return;
mExecutor.execute(() -> {
if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
@@ -268,6 +273,7 @@
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mExecutor == null) return;
mExecutor.execute(() -> {
mTaskInfo.taskDescription = taskInfo.taskDescription;
setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
@@ -276,6 +282,7 @@
@Override
public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mExecutor == null) return;
mExecutor.execute(() -> {
if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
if (mListener != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index 31e1ca8..c410b82 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -34,6 +34,7 @@
import androidx.dynamicanimation.animation.SpringForce;
import com.android.systemui.R;
+import com.android.systemui.bubbles.BadgedImageView;
import com.android.systemui.bubbles.BubblePositioner;
import com.android.systemui.bubbles.BubbleStackView;
import com.android.wm.shell.animation.PhysicsAnimator;
@@ -45,6 +46,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashMap;
+import java.util.List;
import java.util.Set;
import java.util.function.IntSupplier;
@@ -63,6 +65,10 @@
private static final float ANIMATE_IN_STIFFNESS = 1000f;
private static final int ANIMATE_IN_START_DELAY = 25;
+ /** Values to use for animating updated bubble to top of stack. */
+ private static final float BUBBLE_SWAP_SCALE = 0.8f;
+ private static final long BUBBLE_SWAP_DURATION = 300L;
+
/**
* Values to use for the default {@link SpringForce} provided to the physics animation layout.
*/
@@ -180,6 +186,12 @@
/** Horizontal offset of bubbles in the stack. */
private float mStackOffset;
+ /** Offset between stack y and animation y for bubble swap. */
+ private float mSwapAnimationOffset;
+ /** Max number of bubbles to show in the expanded bubble row. */
+ private int mMaxBubbles;
+ /** Default bubble elevation. */
+ private int mElevation;
/** Diameter of the bubble icon. */
private int mBubbleBitmapSize;
/** Width of the bubble (icon and padding). */
@@ -770,6 +782,50 @@
}
}
+ public void animateReorder(List<View> bubbleViews, Runnable after) {
+ for (int newIndex = 0; newIndex < bubbleViews.size(); newIndex++) {
+ View view = bubbleViews.get(newIndex);
+ final int oldIndex= mLayout.indexOfChild(view);
+ animateSwap(view, oldIndex, newIndex, after);
+ }
+ }
+
+ private void animateSwap(View view, int oldIndex, int newIndex, Runnable finishReorder) {
+ final float newY = getStackPosition().y + newIndex * mSwapAnimationOffset;
+ final float swapY = newIndex == 0
+ ? newY - mSwapAnimationOffset // Above top of stack
+ : newY + mSwapAnimationOffset; // Below where bubble will be
+ view.animate()
+ .scaleX(BUBBLE_SWAP_SCALE)
+ .scaleY(BUBBLE_SWAP_SCALE)
+ .translationY(swapY)
+ .setDuration(BUBBLE_SWAP_DURATION)
+ .withEndAction(() -> finishSwapAnimation(view, oldIndex, newIndex, finishReorder));
+ }
+
+ private void finishSwapAnimation(View view, int oldIndex, int newIndex,
+ Runnable finishReorder) {
+
+ // At this point, swapping bubbles have the least overlap.
+ // Update z-index and badge visibility here for least jarring transition.
+ view.setZ((mMaxBubbles * mElevation) - newIndex);
+ BadgedImageView bv = (BadgedImageView) view;
+ if (oldIndex == 0 && newIndex > 0) {
+ bv.hideDotAndBadge(!isStackOnLeftSide());
+ } else if (oldIndex > 0 && newIndex == 0) {
+ bv.showDotAndBadge(!isStackOnLeftSide());
+ }
+
+ // Animate bubble back into stack, at new index and original size.
+ final float newY = getStackPosition().y + newIndex * mStackOffset;
+ view.animate()
+ .scaleX(1f)
+ .scaleY(1f)
+ .translationY(newY)
+ .setDuration(BUBBLE_SWAP_DURATION)
+ .withEndAction(() -> finishReorder.run());
+ }
+
@Override
void onChildReordered(View child, int oldIndex, int newIndex) {
if (isStackPositionSet()) {
@@ -781,6 +837,9 @@
void onActiveControllerForLayout(PhysicsAnimationLayout layout) {
Resources res = layout.getResources();
mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
+ mSwapAnimationOffset = res.getDimensionPixelSize(R.dimen.bubble_swap_animation_offset);
+ mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
+ mElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
mBubbleBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_bitmap_size);
mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index b098579..d73633e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -25,6 +25,7 @@
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.InjectionInflationController;
+import com.android.wm.shell.ShellDump;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
@@ -68,6 +69,9 @@
@BindsInstance
Builder setShellTaskOrganizer(ShellTaskOrganizer s);
+ @BindsInstance
+ Builder setShellDump(Optional<ShellDump> shellDump);
+
SysUIComponent build();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index e3bd1b2..6635286 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -18,8 +18,9 @@
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.wmshell.WMShellModule;
+import com.android.wm.shell.ShellDump;
+import com.android.wm.shell.ShellInit;
import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.splitscreen.SplitScreen;
@@ -47,23 +48,26 @@
* Initializes all the WMShell components before starting any of the SystemUI components.
*/
default void init() {
- // This is to prevent circular init problem by separating registration step out of its
- // constructor. And make sure the initialization of DisplayImeController won't depend on
- // specific feature anymore.
- getDisplayImeController().startMonitorDisplays();
- getShellTaskOrganizer().registerOrganizer();
+ getShellInit().init();
}
- // Required components to be initialized at start up
+ // Gets the Shell init instance
@WMSingleton
- ShellTaskOrganizer getShellTaskOrganizer();
+ ShellInit getShellInit();
+ // Gets the Shell dump instance
@WMSingleton
- DisplayImeController getDisplayImeController();
+ Optional<ShellDump> getShellDump();
+ // TODO(b/162923491): Refactor this out so Pip doesn't need to inject this
@WMSingleton
InputConsumerController getInputConsumerController();
+ // TODO(b/162923491): To be removed once Bubbles migrates over to the Shell
+
+ @WMSingleton
+ ShellTaskOrganizer getShellTaskOrganizer();
+
// TODO(b/162923491): We currently pass the instances through to SysUI, but that may change
// depending on the threading mechanism we go with
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 028870f..8220835 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -44,6 +44,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.logging.nano.MetricsProto;
+import com.android.systemui.biometrics.AuthController;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.sensors.AsyncSensorManager;
@@ -98,7 +99,8 @@
DozeSensors(Context context, AsyncSensorManager sensorManager,
DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog,
- ProximitySensor proximitySensor, SecureSettings secureSettings) {
+ ProximitySensor proximitySensor, SecureSettings secureSettings,
+ AuthController authController) {
mContext = context;
mSensorManager = sensorManager;
mConfig = config;
@@ -152,9 +154,9 @@
dozeLog),
new TriggerSensor(
findSensorWithType(config.udfpsLongPressSensorType()),
- Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
- false /* settingDef */,
- true /* configured */,
+ "doze_pulse_on_auth",
+ true /* settingDef */,
+ authController.hasUdfpsEnrolled() /* configured */,
DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS,
true /* reports touch coordinates */,
true /* touchscreen */,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 45e5c61..c581e85 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -177,7 +177,7 @@
mAllowPulseTriggers = true;
mDozeSensors = new DozeSensors(context, mSensorManager, dozeParameters,
config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor,
- secureSettings);
+ secureSettings, authController);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mDockManager = dockManager;
mProxCheck = proxCheck;
@@ -286,7 +286,7 @@
} else if (isPickup) {
gentleWakeUp(pulseReason);
} else if (isUdfpsLongPress) {
- gentleWakeUp(pulseReason);
+ requestPulse(DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS, true, null);
// Since the gesture won't be received by the UDFPS view, manually inject an
// event.
mAuthController.onAodInterrupt((int) screenX, (int) screenY);
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index e3ee2a1..fff185b 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -120,6 +120,18 @@
return buffer;
}
+ /** Provides a logging buffer for all logs related to privacy indicators in SystemUI. */
+ @Provides
+ @SysUISingleton
+ @PrivacyLog
+ public static LogBuffer providePrivacyLogBuffer(
+ LogcatEchoTracker bufferFilter,
+ DumpManager dumpManager) {
+ LogBuffer buffer = new LogBuffer(("PrivacyLog"), 100, 10, bufferFilter);
+ buffer.attach(dumpManager);
+ return buffer;
+ }
+
/** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
@Provides
@SysUISingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java b/packages/SystemUI/src/com/android/systemui/log/dagger/PrivacyLog.java
similarity index 60%
copy from libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java
copy to packages/SystemUI/src/com/android/systemui/log/dagger/PrivacyLog.java
index 273bd27..e96e532 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/PrivacyLog.java
@@ -14,10 +14,20 @@
* limitations under the License.
*/
-package com.android.wm.shell;
+package com.android.systemui.log.dagger;
-/**
- * Interface for the shell.
- */
-public class WindowManagerShell {
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for privacy indicator-related messages. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface PrivacyLog {
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index d1630eb..d26f7ab 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -109,34 +109,45 @@
super.onBind(device, topMargin, bottomMargin);
final boolean currentlyConnected = isCurrentlyConnected(device);
if (currentlyConnected) {
- mConnectedItem = mFrameLayout;
+ mConnectedItem = mContainerLayout;
+ }
+ mBottomDivider.setVisibility(View.GONE);
+ mCheckBox.setVisibility(View.GONE);
+ if (currentlyConnected && mController.isActiveRemoteDevice(device)) {
+ // Init active device layout
+ mDivider.setVisibility(View.VISIBLE);
+ mDivider.setTransitionAlpha(1);
+ mAddIcon.setVisibility(View.VISIBLE);
+ mAddIcon.setTransitionAlpha(1);
+ mAddIcon.setOnClickListener(v -> onEndItemClick());
+ } else {
+ // Init non-active device layout
+ mDivider.setVisibility(View.GONE);
+ mAddIcon.setVisibility(View.GONE);
}
if (mController.isTransferring()) {
if (device.getState() == MediaDeviceState.STATE_CONNECTING
&& !mController.hasAdjustVolumeUserRestriction()) {
- setTwoLineLayout(device, null /* title */, true /* bFocused */,
- false /* showSeekBar*/, true /* showProgressBar */,
- false /* showSubtitle */);
+ setTwoLineLayout(device, true /* bFocused */, false /* showSeekBar*/,
+ true /* showProgressBar */, false /* showSubtitle */);
} else {
setSingleLineLayout(getItemTitle(device), false /* bFocused */);
}
} else {
// Set different layout for each device
if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) {
- setTwoLineLayout(device, null /* title */, false /* bFocused */,
- false /* showSeekBar*/, false /* showProgressBar */,
+ setTwoLineLayout(device, false /* bFocused */,
+ false /* showSeekBar */, false /* showProgressBar */,
true /* showSubtitle */);
mSubTitleText.setText(R.string.media_output_dialog_connect_failed);
- mFrameLayout.setOnClickListener(v -> onItemClick(v, device));
- } else if (!mController.hasAdjustVolumeUserRestriction()
- && currentlyConnected) {
- setTwoLineLayout(device, null /* title */, true /* bFocused */,
- true /* showSeekBar*/, false /* showProgressBar */,
- false /* showSubtitle */);
+ mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
+ } else if (!mController.hasAdjustVolumeUserRestriction() && currentlyConnected) {
+ setTwoLineLayout(device, true /* bFocused */, true /* showSeekBar */,
+ false /* showProgressBar */, false /* showSubtitle */);
initSeekbar(device);
} else {
setSingleLineLayout(getItemTitle(device), false /* bFocused */);
- mFrameLayout.setOnClickListener(v -> onItemClick(v, device));
+ mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
}
}
}
@@ -145,13 +156,17 @@
void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
super.onBind(customizedItem, topMargin, bottomMargin);
if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
+ mCheckBox.setVisibility(View.GONE);
+ mDivider.setVisibility(View.GONE);
+ mAddIcon.setVisibility(View.GONE);
+ mBottomDivider.setVisibility(View.GONE);
setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new),
false /* bFocused */);
final Drawable d = mContext.getDrawable(R.drawable.ic_add);
d.setColorFilter(new PorterDuffColorFilter(
Utils.getColorAccentDefaultColor(mContext), PorterDuff.Mode.SRC_IN));
mTitleIcon.setImageDrawable(d);
- mFrameLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW));
+ mContainerLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW));
}
}
@@ -173,5 +188,9 @@
mController.launchBluetoothPairing();
}
}
+
+ private void onEndItemClick() {
+ mController.launchMediaOutputGroupDialog();
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 2d3e77d..536b759 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -24,8 +24,9 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.FrameLayout;
+import android.widget.CheckBox;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
@@ -44,19 +45,17 @@
public abstract class MediaOutputBaseAdapter extends
RecyclerView.Adapter<MediaOutputBaseAdapter.MediaDeviceBaseViewHolder> {
- private static final String FONT_SELECTED_TITLE = "sans-serif-medium";
- private static final String FONT_TITLE = "sans-serif";
-
static final int CUSTOMIZED_ITEM_PAIR_NEW = 1;
+ static final int CUSTOMIZED_ITEM_GROUP = 2;
final MediaOutputController mController;
- private boolean mIsDragging;
private int mMargin;
private boolean mIsAnimating;
Context mContext;
View mHolderView;
+ boolean mIsDragging;
public MediaOutputBaseAdapter(MediaOutputController controller) {
mController = controller;
@@ -99,27 +98,33 @@
private static final int ANIM_DURATION = 200;
- final FrameLayout mFrameLayout;
+ final LinearLayout mContainerLayout;
final TextView mTitleText;
final TextView mTwoLineTitleText;
final TextView mSubTitleText;
final ImageView mTitleIcon;
- final ImageView mEndIcon;
+ final ImageView mAddIcon;
final ProgressBar mProgressBar;
final SeekBar mSeekBar;
final RelativeLayout mTwoLineLayout;
+ final View mDivider;
+ final View mBottomDivider;
+ final CheckBox mCheckBox;
MediaDeviceBaseViewHolder(View view) {
super(view);
- mFrameLayout = view.requireViewById(R.id.device_container);
+ mContainerLayout = view.requireViewById(R.id.device_container);
mTitleText = view.requireViewById(R.id.title);
mSubTitleText = view.requireViewById(R.id.subtitle);
mTwoLineLayout = view.requireViewById(R.id.two_line_layout);
mTwoLineTitleText = view.requireViewById(R.id.two_line_title);
mTitleIcon = view.requireViewById(R.id.title_icon);
- mEndIcon = view.requireViewById(R.id.end_icon);
mProgressBar = view.requireViewById(R.id.volume_indeterminate_progress);
mSeekBar = view.requireViewById(R.id.volume_seekbar);
+ mDivider = view.requireViewById(R.id.end_divider);
+ mBottomDivider = view.requireViewById(R.id.bottom_divider);
+ mAddIcon = view.requireViewById(R.id.add_icon);
+ mCheckBox = view.requireViewById(R.id.check_box);
}
void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
@@ -132,11 +137,11 @@
}
private void setMargin(boolean topMargin, boolean bottomMargin) {
- ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mFrameLayout
+ ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mContainerLayout
.getLayoutParams();
params.topMargin = topMargin ? mMargin : 0;
params.bottomMargin = bottomMargin ? mMargin : 0;
- mFrameLayout.setLayoutParams(params);
+ mContainerLayout.setLayoutParams(params);
}
void setSingleLineLayout(CharSequence title, boolean bFocused) {
@@ -146,13 +151,26 @@
mTitleText.setTranslationY(0);
mTitleText.setText(title);
if (bFocused) {
- mTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, Typeface.NORMAL));
+ mTitleText.setTypeface(Typeface.create(mContext.getString(
+ com.android.internal.R.string.config_headlineFontFamilyMedium),
+ Typeface.NORMAL));
} else {
- mTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL));
+ mTitleText.setTypeface(Typeface.create(mContext.getString(
+ com.android.internal.R.string.config_headlineFontFamily), Typeface.NORMAL));
}
}
- void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused,
+ void setTwoLineLayout(MediaDevice device, boolean bFocused, boolean showSeekBar,
+ boolean showProgressBar, boolean showSubtitle) {
+ setTwoLineLayout(device, null, bFocused, showSeekBar, showProgressBar, showSubtitle);
+ }
+
+ void setTwoLineLayout(CharSequence title, boolean bFocused, boolean showSeekBar,
+ boolean showProgressBar, boolean showSubtitle) {
+ setTwoLineLayout(null, title, bFocused, showSeekBar, showProgressBar, showSubtitle);
+ }
+
+ private void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused,
boolean showSeekBar, boolean showProgressBar, boolean showSubtitle) {
mTitleText.setVisibility(View.GONE);
mTwoLineLayout.setVisibility(View.VISIBLE);
@@ -168,18 +186,21 @@
}
if (bFocused) {
- mTwoLineTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE,
+ mTwoLineTitleText.setTypeface(Typeface.create(mContext.getString(
+ com.android.internal.R.string.config_headlineFontFamilyMedium),
Typeface.NORMAL));
} else {
- mTwoLineTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL));
+ mTwoLineTitleText.setTypeface(Typeface.create(mContext.getString(
+ com.android.internal.R.string.config_headlineFontFamily), Typeface.NORMAL));
}
}
void initSeekbar(MediaDevice device) {
mSeekBar.setMax(device.getMaxVolume());
mSeekBar.setMin(0);
- if (mSeekBar.getProgress() != device.getCurrentVolume()) {
- mSeekBar.setProgress(device.getCurrentVolume());
+ final int currentVolume = device.getCurrentVolume();
+ if (mSeekBar.getProgress() != currentVolume) {
+ mSeekBar.setProgress(currentVolume);
}
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
@@ -213,7 +234,9 @@
}
mIsAnimating = true;
// Animation for title text
- toTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, Typeface.NORMAL));
+ toTitleText.setTypeface(Typeface.create(mContext.getString(
+ com.android.internal.R.string.config_headlineFontFamilyMedium),
+ Typeface.NORMAL));
toTitleText.animate()
.setDuration(ANIM_DURATION)
.translationY(-delta)
@@ -234,7 +257,9 @@
public void onAnimationEnd(Animator animation) {
final TextView fromTitleText = from.requireViewById(
R.id.two_line_title);
- fromTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL));
+ fromTitleText.setTypeface(Typeface.create(mContext.getString(
+ com.android.internal.R.string.config_headlineFontFamily),
+ Typeface.NORMAL));
fromTitleText.animate()
.setDuration(ANIM_DURATION)
.translationY(delta)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index caef536..78939df 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -119,6 +119,8 @@
// Init device list
mDevicesRecyclerView.setLayoutManager(mLayoutManager);
mDevicesRecyclerView.setAdapter(mAdapter);
+ // Init header icon
+ mHeaderIcon.setOnClickListener(v -> onHeaderIconClick());
// Init bottom buttons
mDoneButton.setOnClickListener(v -> dismiss());
mStopButton.setOnClickListener(v -> {
@@ -218,4 +220,7 @@
dismiss();
}
}
+
+ void onHeaderIconClick() {
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index b1f1bda..80928d6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -18,10 +18,7 @@
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.MediaMetadata;
import android.media.MediaRoute2Info;
@@ -49,6 +46,8 @@
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.ShadeController;
import java.util.ArrayList;
@@ -61,7 +60,7 @@
/**
* Controller for media output dialog
*/
-public class MediaOutputController implements LocalMediaManager.DeviceCallback{
+public class MediaOutputController implements LocalMediaManager.DeviceCallback {
private static final String TAG = "MediaOutputController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -71,6 +70,9 @@
private final MediaSessionManager mMediaSessionManager;
private final ShadeController mShadeController;
private final ActivityStarter mActivityStarter;
+ private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
+ private final boolean mAboveStatusbar;
+ private final NotificationEntryManager mNotificationEntryManager;
@VisibleForTesting
final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
@@ -82,13 +84,16 @@
@Inject
public MediaOutputController(@NonNull Context context, String packageName,
- MediaSessionManager mediaSessionManager, LocalBluetoothManager
- lbm, ShadeController shadeController, ActivityStarter starter) {
+ boolean aboveStatusbar, MediaSessionManager mediaSessionManager, LocalBluetoothManager
+ lbm, ShadeController shadeController, ActivityStarter starter,
+ NotificationEntryManager notificationEntryManager) {
mContext = context;
mPackageName = packageName;
mMediaSessionManager = mediaSessionManager;
mShadeController = shadeController;
mActivityStarter = starter;
+ mAboveStatusbar = aboveStatusbar;
+ mNotificationEntryManager = notificationEntryManager;
InfoMediaManager imm = new InfoMediaManager(mContext, packageName, null, lbm);
mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
}
@@ -194,7 +199,7 @@
if (DEBUG) {
Log.d(TAG, "Media meta data does not contain icon information");
}
- return getPackageIcon();
+ return getNotificationIcon();
}
IconCompat getDeviceIconCompat(MediaDevice device) {
@@ -210,24 +215,15 @@
return BluetoothUtils.createIconWithDrawable(drawable);
}
- private IconCompat getPackageIcon() {
+ IconCompat getNotificationIcon() {
if (TextUtils.isEmpty(mPackageName)) {
return null;
}
- try {
- final Drawable drawable = mContext.getPackageManager().getApplicationIcon(mPackageName);
- if (drawable instanceof BitmapDrawable) {
- return IconCompat.createWithBitmap(((BitmapDrawable) drawable).getBitmap());
- }
- final Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
- drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
- final Canvas canvas = new Canvas(bitmap);
- drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- drawable.draw(canvas);
- return IconCompat.createWithBitmap(bitmap);
- } catch (PackageManager.NameNotFoundException e) {
- if (DEBUG) {
- Log.e(TAG, "Package is not found. Unable to get package icon.");
+ for (NotificationEntry entry
+ : mNotificationEntryManager.getActiveNotificationsForCurrentUser()) {
+ if (entry.getSbn().getNotification().hasMediaSession()
+ && TextUtils.equals(entry.getSbn().getPackageName(), mPackageName)) {
+ return IconCompat.createFromIcon(entry.getSbn().getNotification().getLargeIcon());
}
}
return null;
@@ -271,6 +267,42 @@
mMediaDevices.addAll(targetMediaDevices);
}
+ List<MediaDevice> getGroupMediaDevices() {
+ final List<MediaDevice> selectedDevices = getSelectedMediaDevice();
+ final List<MediaDevice> selectableDevices = getSelectableMediaDevice();
+ if (mGroupMediaDevices.isEmpty()) {
+ mGroupMediaDevices.addAll(selectedDevices);
+ mGroupMediaDevices.addAll(selectableDevices);
+ return mGroupMediaDevices;
+ }
+ // To keep the same list order
+ final Collection<MediaDevice> sourceDevices = new ArrayList<>();
+ final Collection<MediaDevice> targetMediaDevices = new ArrayList<>();
+ sourceDevices.addAll(selectedDevices);
+ sourceDevices.addAll(selectableDevices);
+ for (MediaDevice originalDevice : mGroupMediaDevices) {
+ for (MediaDevice newDevice : sourceDevices) {
+ if (TextUtils.equals(originalDevice.getId(), newDevice.getId())) {
+ targetMediaDevices.add(newDevice);
+ sourceDevices.remove(newDevice);
+ break;
+ }
+ }
+ }
+ // Add new devices at the end of list if necessary
+ if (!sourceDevices.isEmpty()) {
+ targetMediaDevices.addAll(sourceDevices);
+ }
+ mGroupMediaDevices.clear();
+ mGroupMediaDevices.addAll(targetMediaDevices);
+
+ return mGroupMediaDevices;
+ }
+
+ void resetGroupMediaDevices() {
+ mGroupMediaDevices.clear();
+ }
+
void connectDevice(MediaDevice device) {
ThreadUtils.postOnBackgroundThread(() -> {
mLocalMediaManager.connectDevice(device);
@@ -309,15 +341,6 @@
return mLocalMediaManager.getDeselectableMediaDevice();
}
- boolean isDeviceIncluded(Collection<MediaDevice> deviceCollection, MediaDevice targetDevice) {
- for (MediaDevice device : deviceCollection) {
- if (TextUtils.equals(device.getId(), targetDevice.getId())) {
- return true;
- }
- }
- return false;
- }
-
void adjustSessionVolume(String sessionId, int volume) {
mLocalMediaManager.adjustSessionVolume(sessionId, volume);
}
@@ -407,6 +430,16 @@
mActivityStarter.dismissKeyguardThenExecute(postKeyguardAction, null, true);
}
+ void launchMediaOutputDialog() {
+ mCallback.dismissDialog();
+ new MediaOutputDialog(mContext, mAboveStatusbar, this);
+ }
+
+ void launchMediaOutputGroupDialog() {
+ mCallback.dismissDialog();
+ new MediaOutputGroupDialog(mContext, mAboveStatusbar, this);
+ }
+
boolean isActiveRemoteDevice(@NonNull MediaDevice device) {
final List<String> features = device.getFeatures();
return (features.contains(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index 4cdca4c..7d1a7ce 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -20,6 +20,7 @@
import android.media.session.MediaSessionManager
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.phone.ShadeController
import javax.inject.Inject
@@ -31,7 +32,8 @@
private val mediaSessionManager: MediaSessionManager,
private val lbm: LocalBluetoothManager?,
private val shadeController: ShadeController,
- private val starter: ActivityStarter
+ private val starter: ActivityStarter,
+ private val notificationEntryManager: NotificationEntryManager
) {
companion object {
var mediaOutputDialog: MediaOutputDialog? = null
@@ -40,9 +42,8 @@
/** Creates a [MediaOutputDialog] for the given package. */
fun create(packageName: String, aboveStatusBar: Boolean) {
mediaOutputDialog?.dismiss()
-
- mediaOutputDialog = MediaOutputController(context, packageName, mediaSessionManager, lbm,
- shadeController, starter).run {
+ mediaOutputDialog = MediaOutputController(context, packageName, aboveStatusBar,
+ mediaSessionManager, lbm, shadeController, starter, notificationEntryManager).run {
MediaOutputDialog(context, aboveStatusBar, this) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
new file mode 100644
index 0000000..ceb4495
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
@@ -0,0 +1,220 @@
+/*
+ * 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.media.dialog;
+
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.SeekBar;
+
+import androidx.annotation.NonNull;
+
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.R;
+
+import java.util.List;
+
+/**
+ * Adapter for media output dynamic group dialog.
+ */
+public class MediaOutputGroupAdapter extends MediaOutputBaseAdapter {
+
+ private static final String TAG = "MediaOutputGroupAdapter";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final List<MediaDevice> mGroupMediaDevices;
+
+ public MediaOutputGroupAdapter(MediaOutputController controller) {
+ super(controller);
+ mGroupMediaDevices = controller.getGroupMediaDevices();
+ }
+
+ @Override
+ public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
+ int viewType) {
+ super.onCreateViewHolder(viewGroup, viewType);
+
+ return new GroupViewHolder(mHolderView);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull MediaDeviceBaseViewHolder viewHolder, int position) {
+ // Add "Group"
+ if (position == 0) {
+ viewHolder.onBind(CUSTOMIZED_ITEM_GROUP, true /* topMargin */,
+ false /* bottomMargin */);
+ return;
+ }
+ // Add available devices
+ final int newPosition = position - 1;
+ final int size = mGroupMediaDevices.size();
+ if (newPosition < size) {
+ viewHolder.onBind(mGroupMediaDevices.get(newPosition), false /* topMargin */,
+ newPosition == (size - 1) /* bottomMargin */);
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Incorrect position: " + position);
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ // Require extra item for group volume operation
+ return mGroupMediaDevices.size() + 1;
+ }
+
+ @Override
+ CharSequence getItemTitle(MediaDevice device) {
+ return super.getItemTitle(device);
+ }
+
+ class GroupViewHolder extends MediaDeviceBaseViewHolder {
+
+ GroupViewHolder(View view) {
+ super(view);
+ }
+
+ @Override
+ void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
+ super.onBind(device, topMargin, bottomMargin);
+ mDivider.setVisibility(View.GONE);
+ mAddIcon.setVisibility(View.GONE);
+ mBottomDivider.setVisibility(View.GONE);
+ mCheckBox.setVisibility(View.VISIBLE);
+ mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ onCheckBoxClicked(isChecked, device);
+ });
+ setTwoLineLayout(device, false /* bFocused */, true /* showSeekBar */,
+ false /* showProgressBar */, false /* showSubtitle*/);
+ initSeekbar(device);
+ final List<MediaDevice> selectedDevices = mController.getSelectedMediaDevice();
+ if (isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
+ mCheckBox.setButtonDrawable(R.drawable.ic_check_box);
+ mCheckBox.setChecked(false);
+ mCheckBox.setEnabled(true);
+ } else if (isDeviceIncluded(selectedDevices, device)) {
+ if (selectedDevices.size() == 1 || !isDeviceIncluded(
+ mController.getDeselectableMediaDevice(), device)) {
+ mCheckBox.setButtonDrawable(getDisabledCheckboxDrawable());
+ mCheckBox.setChecked(true);
+ mCheckBox.setEnabled(false);
+ } else {
+ mCheckBox.setButtonDrawable(R.drawable.ic_check_box);
+ mCheckBox.setChecked(true);
+ mCheckBox.setEnabled(true);
+ }
+ }
+ }
+
+ @Override
+ void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
+ super.onBind(customizedItem, topMargin, bottomMargin);
+ if (customizedItem == CUSTOMIZED_ITEM_GROUP) {
+ setTwoLineLayout(mContext.getText(R.string.media_output_dialog_group),
+ true /* bFocused */, true /* showSeekBar */, false /* showProgressBar */,
+ false /* showSubtitle*/);
+ mTitleIcon.setImageDrawable(getSpeakerDrawable());
+ mBottomDivider.setVisibility(View.VISIBLE);
+ mCheckBox.setVisibility(View.GONE);
+ mDivider.setVisibility(View.GONE);
+ mAddIcon.setVisibility(View.GONE);
+ initSessionSeekbar();
+ }
+ }
+
+ private void onCheckBoxClicked(boolean isChecked, MediaDevice device) {
+ if (isChecked && isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
+ mController.addDeviceToPlayMedia(device);
+ } else if (!isChecked && isDeviceIncluded(mController.getDeselectableMediaDevice(),
+ device)) {
+ mController.removeDeviceFromPlayMedia(device);
+ }
+ }
+
+ private void initSessionSeekbar() {
+ mSeekBar.setMax(mController.getSessionVolumeMax());
+ mSeekBar.setMin(0);
+ final int currentVolume = mController.getSessionVolume();
+ if (mSeekBar.getProgress() != currentVolume) {
+ mSeekBar.setProgress(currentVolume);
+ }
+ mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (!fromUser) {
+ return;
+ }
+ mController.adjustSessionVolume(progress);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ mIsDragging = true;
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ mIsDragging = false;
+ }
+ });
+ }
+
+ private Drawable getDisabledCheckboxDrawable() {
+ final Drawable drawable = mContext.getDrawable(R.drawable.ic_check_box_blue_24dp)
+ .mutate();
+ final Bitmap checkbox = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+ drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(checkbox);
+ TypedValue value = new TypedValue();
+ mContext.getTheme().resolveAttribute(android.R.attr.disabledAlpha, value, true);
+ drawable.setAlpha((int) (value.getFloat() * 255));
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+
+ return drawable;
+ }
+
+ private Drawable getSpeakerDrawable() {
+ final Drawable drawable = mContext.getDrawable(R.drawable.ic_speaker_group_black_24dp)
+ .mutate();
+ final ColorStateList list = mContext.getResources().getColorStateList(
+ R.color.advanced_icon_color, mContext.getTheme());
+ drawable.setColorFilter(new PorterDuffColorFilter(list.getDefaultColor(),
+ PorterDuff.Mode.SRC_IN));
+ return BluetoothUtils.buildAdvancedDrawable(mContext, drawable);
+ }
+
+ private boolean isDeviceIncluded(List<MediaDevice> deviceList, MediaDevice targetDevice) {
+ for (MediaDevice device : deviceList) {
+ if (TextUtils.equals(device.getId(), targetDevice.getId())) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
new file mode 100644
index 0000000..4079304
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
@@ -0,0 +1,88 @@
+/*
+ * 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.media.dialog;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager;
+
+import androidx.core.graphics.drawable.IconCompat;
+
+import com.android.systemui.R;
+
+/**
+ * Dialog for media output group.
+ */
+public class MediaOutputGroupDialog extends MediaOutputBaseDialog {
+
+ MediaOutputGroupDialog(Context context, boolean aboveStatusbar, MediaOutputController
+ mediaOutputController) {
+ super(context, mediaOutputController);
+ mMediaOutputController.resetGroupMediaDevices();
+ mAdapter = new MediaOutputGroupAdapter(mMediaOutputController);
+ if (!aboveStatusbar) {
+ getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+ }
+ show();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ int getHeaderIconRes() {
+ return R.drawable.ic_arrow_back;
+ }
+
+ @Override
+ IconCompat getHeaderIcon() {
+ return null;
+ }
+
+ @Override
+ int getHeaderIconSize() {
+ return mContext.getResources().getDimensionPixelSize(
+ R.dimen.media_output_dialog_header_back_icon_size);
+ }
+
+ @Override
+ CharSequence getHeaderText() {
+ return mContext.getString(R.string.media_output_dialog_add_output);
+ }
+
+ @Override
+ CharSequence getHeaderSubtitle() {
+ final int size = mMediaOutputController.getSelectedMediaDevice().size();
+ if (size == 1) {
+ return mContext.getText(R.string.media_output_dialog_single_device);
+ }
+ return mContext.getString(R.string.media_output_dialog_multiple_devices, size);
+ }
+
+ @Override
+ int getStopButtonVisibility() {
+ return View.VISIBLE;
+ }
+
+ @Override
+ void onHeaderIconClick() {
+ mMediaOutputController.launchMediaOutputDialog();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 9d46b32..1a9dd71 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -50,7 +50,7 @@
IPeopleManager peopleManager
) throws Exception {
boolean showAllConversations = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE) == 0;
+ Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
List<ConversationChannelWrapper> conversations =
notificationManager.getConversations(
!showAllConversations /* priority only */).getList();
@@ -69,16 +69,29 @@
/** Converts {@code drawable} to a {@link Bitmap}. */
public static Bitmap convertDrawableToBitmap(Drawable drawable) {
- if (drawable instanceof BitmapDrawable) {
- return ((BitmapDrawable) drawable).getBitmap();
+ if (drawable == null) {
+ return null;
}
- // We use max below because the drawable might have no intrinsic width/height (e.g. if the
- // drawable is a solid color).
- Bitmap bitmap =
- Bitmap.createBitmap(
- Math.max(drawable.getIntrinsicWidth(), 1),
- Math.max(drawable.getIntrinsicHeight(), 1),
- Bitmap.Config.ARGB_8888);
+
+ if (drawable instanceof BitmapDrawable) {
+ BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
+ if (bitmapDrawable.getBitmap() != null) {
+ return bitmapDrawable.getBitmap();
+ }
+ }
+
+ Bitmap bitmap;
+ if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
+ bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ // Single color bitmap will be created of 1x1 pixel
+ } else {
+ bitmap = Bitmap.createBitmap(
+ drawable.getIntrinsicWidth(),
+ drawable.getIntrinsicHeight(),
+ Bitmap.Config.ARGB_8888
+ );
+ }
+
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
new file mode 100644
index 0000000..44f173b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
@@ -0,0 +1,59 @@
+/*
+ * 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.people.widget;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.systemui.people.PeopleSpaceUtils;
+
+/** Proxy activity to launch ShortcutInfo's conversation. */
+public class LaunchConversationActivity extends Activity {
+ private static final String TAG = "PeopleSpaceLaunchConv";
+ private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (DEBUG) Log.d(TAG, "onCreate called");
+
+ Intent intent = getIntent();
+ ShortcutInfo shortcutInfo = (ShortcutInfo) intent.getParcelableExtra(
+ PeopleSpaceWidgetProvider.EXTRA_SHORTCUT_INFO
+ );
+ if (shortcutInfo != null) {
+ if (DEBUG) {
+ Log.d(TAG, "Launching conversation with shortcutInfo id " + shortcutInfo.getId());
+ }
+ try {
+ LauncherApps launcherApps =
+ getApplicationContext().getSystemService(LauncherApps.class);
+ launcherApps.startShortcut(
+ shortcutInfo, null, null);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception starting shortcut:" + e);
+ }
+ } else {
+ if (DEBUG) Log.d(TAG, "Trying to launch conversation with null shortcutInfo.");
+ }
+ finish();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
index 85801f9..aa98b61 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
@@ -16,6 +16,7 @@
package com.android.systemui.people.widget;
+import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
@@ -31,6 +32,8 @@
private static final String TAG = "PeopleSpaceWidgetPvd";
private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
+ public static final String EXTRA_SHORTCUT_INFO = "extra_shortcut_info";
+
/** Called when widget updates. */
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
@@ -45,6 +48,19 @@
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
views.setRemoteAdapter(R.id.widget_list_view, intent);
+ Intent activityIntent = new Intent(context, LaunchConversationActivity.class);
+ activityIntent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_ACTIVITY_NO_HISTORY
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ PendingIntent pendingIntent = PendingIntent.getActivity(
+ context,
+ appWidgetId,
+ activityIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
+ views.setPendingIntentTemplate(R.id.widget_list_view, pendingIntent);
+
// Tell the AppWidgetManager to perform an update on the current app widget
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_list_view);
appWidgetManager.updateAppWidget(appWidgetId, views);
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
index 093925a..c68c306 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
@@ -130,6 +130,10 @@
mLauncherApps.getShortcutIconDrawable(shortcutInfo, 0)
)
);
+
+ Intent fillInIntent = new Intent();
+ fillInIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_SHORTCUT_INFO, shortcutInfo);
+ personView.setOnClickFillInIntent(R.id.item, fillInIntent);
} catch (Exception e) {
Log.e(TAG, "Couldn't retrieve shortcut information", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
index 3da1363..7359e79 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -19,20 +19,31 @@
typealias Privacy = PrivacyType
-enum class PrivacyType(val nameId: Int, val iconId: Int) {
+enum class PrivacyType(val nameId: Int, val iconId: Int, val logName: String) {
// This is uses the icons used by the corresponding permission groups in the AndroidManifest
- TYPE_CAMERA(R.string.privacy_type_camera,
- com.android.internal.R.drawable.perm_group_camera),
- TYPE_MICROPHONE(R.string.privacy_type_microphone,
- com.android.internal.R.drawable.perm_group_microphone),
- TYPE_LOCATION(R.string.privacy_type_location,
- com.android.internal.R.drawable.perm_group_location);
+ TYPE_CAMERA(
+ R.string.privacy_type_camera,
+ com.android.internal.R.drawable.perm_group_camera,
+ "camera"
+ ),
+ TYPE_MICROPHONE(
+ R.string.privacy_type_microphone,
+ com.android.internal.R.drawable.perm_group_microphone,
+ "microphone"
+ ),
+ TYPE_LOCATION(
+ R.string.privacy_type_location,
+ com.android.internal.R.drawable.perm_group_location,
+ "location"
+ );
fun getName(context: Context) = context.resources.getString(nameId)
fun getIcon(context: Context) = context.resources.getDrawable(iconId, context.theme)
}
-data class PrivacyItem(val privacyType: PrivacyType, val application: PrivacyApplication)
+data class PrivacyItem(val privacyType: PrivacyType, val application: PrivacyApplication) {
+ fun toLog(): String = "(${privacyType.logName}, ${application.packageName}(${application.uid}))"
+}
data class PrivacyApplication(val packageName: String, val uid: Int)
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index dc5ba69..87ffbd4 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -32,6 +32,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.DeviceConfigProxy
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -48,6 +49,7 @@
@Background private val bgExecutor: Executor,
private val deviceConfigProxy: DeviceConfigProxy,
private val userTracker: UserTracker,
+ private val logger: PrivacyLogger,
dumpManager: DumpManager
) : Dumpable {
@@ -158,6 +160,7 @@
}
val userId = UserHandle.getUserId(uid)
if (userId in currentUserIds) {
+ logger.logUpdatedItemFromAppOps(code, uid, packageName, active)
update(false)
}
}
@@ -194,6 +197,7 @@
bgExecutor.execute {
if (updateUsers) {
currentUserIds = userTracker.userProfiles.map { it.id }
+ logger.logCurrentProfilesChanged(currentUserIds)
}
updateListAndNotifyChanges.run()
}
@@ -260,6 +264,8 @@
}
val list = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
.mapNotNull { toPrivacyItem(it) }.distinct()
+ logger.logUpdatedPrivacyItemsList(
+ list.joinToString(separator = ", ", transform = PrivacyItem::toLog))
privacyList = list
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
new file mode 100644
index 0000000..c88676e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.privacy.logging
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.LogMessage
+import com.android.systemui.log.dagger.PrivacyLog
+import javax.inject.Inject
+
+private const val TAG = "PrivacyLog"
+
+class PrivacyLogger @Inject constructor(
+ @PrivacyLog private val buffer: LogBuffer
+) {
+
+ fun logUpdatedItemFromAppOps(code: Int, uid: Int, packageName: String, active: Boolean) {
+ log(LogLevel.INFO, {
+ int1 = code
+ int2 = uid
+ str1 = packageName
+ bool1 = active
+ }, {
+ "App Op: $int1 for $str1($int2), active=$bool1"
+ })
+ }
+
+ fun logUpdatedPrivacyItemsList(listAsString: String) {
+ log(LogLevel.INFO, {
+ str1 = listAsString
+ }, {
+ "Updated list: $str1"
+ })
+ }
+
+ fun logCurrentProfilesChanged(profiles: List<Int>) {
+ log(LogLevel.INFO, {
+ str1 = profiles.toString()
+ }, {
+ "Profiles changed: $str1"
+ })
+ }
+
+ fun logChipVisible(visible: Boolean) {
+ log(LogLevel.INFO, {
+ bool1 = visible
+ }, {
+ "Chip visible: $bool1"
+ })
+ }
+
+ fun logStatusBarIconsVisible(
+ showCamera: Boolean,
+ showMichrophone: Boolean,
+ showLocation: Boolean
+ ) {
+ log(LogLevel.INFO, {
+ bool1 = showCamera
+ bool2 = showMichrophone
+ bool3 = showLocation
+ }, {
+ "Status bar icons visible: camera=$bool1, microphone=$bool2, location=$bool3"
+ })
+ }
+
+ private inline fun log(
+ logLevel: LogLevel,
+ initializer: LogMessage.() -> Unit,
+ noinline printer: LogMessage.() -> String
+ ) {
+ buffer.log(TAG, logLevel, initializer, printer)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 7e2433a..8e0e4ac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -205,12 +205,11 @@
mQSPanelContainer.setLayoutParams(layoutParams);
mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
- mContentPaddingStart = getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.notification_content_margin_start);
- int newPaddingEnd = getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.notification_content_margin_end);
- boolean marginsChanged = newPaddingEnd != mContentPaddingEnd;
- mContentPaddingEnd = newPaddingEnd;
+ int padding = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_shade_content_margin_horizontal);
+ boolean marginsChanged = padding != mContentPaddingStart || padding != mContentPaddingEnd;
+ mContentPaddingStart = padding;
+ mContentPaddingEnd = padding;
if (marginsChanged) {
updatePaddingsAndMargins();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
index acead98..4b9f431 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
@@ -34,8 +34,7 @@
}
@Override
- public void init() {
- super.init();
+ public void onInit() {
mQuickStatusBarHeaderController.init();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index febb71c..32904a2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -43,6 +43,7 @@
import com.android.systemui.privacy.PrivacyChipEvent;
import com.android.systemui.privacy.PrivacyItem;
import com.android.systemui.privacy.PrivacyItemController;
+import com.android.systemui.privacy.logging.PrivacyLogger;
import com.android.systemui.qs.carrier.QSCarrierGroupController;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.settings.UserTracker;
@@ -90,6 +91,7 @@
private final StatusIconContainer mIconContainer;
private final StatusBarIconController.TintedIconManager mIconManager;
private final DemoMode mDemoModeReceiver;
+ private final PrivacyLogger mPrivacyLogger;
private boolean mListening;
private AlarmClockInfo mNextAlarm;
@@ -213,7 +215,8 @@
QSTileHost qsTileHost, StatusBarIconController statusBarIconController,
CommandQueue commandQueue, DemoModeController demoModeController,
UserTracker userTracker, QuickQSPanelController quickQSPanelController,
- QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder) {
+ QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder,
+ PrivacyLogger privacyLogger) {
super(view);
mZenModeController = zenModeController;
mNextAlarmController = nextAlarmController;
@@ -228,6 +231,7 @@
mUserTracker = userTracker;
mLifecycle = new LifecycleRegistry(mLifecycleOwner);
mHeaderQsPanelController = quickQSPanelController;
+ mPrivacyLogger = privacyLogger;
mQSCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(mView.findViewById(R.id.carrier_group))
@@ -257,15 +261,15 @@
mRingerContainer.setOnClickListener(mOnClickListener);
mPrivacyChip.setOnClickListener(mOnClickListener);
- // Ignore privacy icons because they show in the space above QQS
- mIconContainer.addIgnoredSlots(getIgnoredIconSlots());
- mIconContainer.setShouldRestrictIcons(false);
- mStatusBarIconController.addIconGroup(mIconManager);
-
mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
+ // Ignore privacy icons because they show in the space above QQS
+ mIconContainer.setIgnoredSlots(getIgnoredIconSlots());
+ mIconContainer.setShouldRestrictIcons(false);
+ mStatusBarIconController.addIconGroup(mIconManager);
+
setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
mView.onAttach(mIconManager);
@@ -323,6 +327,7 @@
private void setChipVisibility(boolean chipVisible) {
if (chipVisible && getChipEnabled()) {
mPrivacyChip.setVisibility(View.VISIBLE);
+ mPrivacyLogger.logChipVisible(true);
// Makes sure that the chip is logged as viewed at most once each time QS is opened
// mListening makes sure that the callback didn't return after the user closed QS
if (!mPrivacyChipLogged && mListening) {
@@ -330,6 +335,7 @@
mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_VIEW);
}
} else {
+ mPrivacyLogger.logChipVisible(false);
mPrivacyChip.setVisibility(View.GONE);
}
}
@@ -337,16 +343,17 @@
private List<String> getIgnoredIconSlots() {
ArrayList<String> ignored = new ArrayList<>();
if (getChipEnabled()) {
- ignored.add(mView.getResources().getString(
- com.android.internal.R.string.status_bar_camera));
- ignored.add(mView.getResources().getString(
- com.android.internal.R.string.status_bar_microphone));
+ if (mAllIndicatorsEnabled || mMicCameraIndicatorsEnabled) {
+ ignored.add(mView.getResources().getString(
+ com.android.internal.R.string.status_bar_camera));
+ ignored.add(mView.getResources().getString(
+ com.android.internal.R.string.status_bar_microphone));
+ }
if (mAllIndicatorsEnabled || mLocationIndicatorsEnabled) {
ignored.add(mView.getResources().getString(
com.android.internal.R.string.status_bar_location));
}
}
-
return ignored;
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index c0061ad..b2ebf3f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -281,8 +281,10 @@
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// cancel current pending intent (if any) since clipData isn't used for matching
- PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, 0,
- sharingChooserIntent, PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
+ PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
+ context, 0, sharingChooserIntent,
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
+ null, UserHandle.CURRENT);
// Create a share action for the notification
PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, requestCode,
@@ -294,7 +296,8 @@
mSmartActionsEnabled)
.setAction(Intent.ACTION_SEND)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
- PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
+ UserHandle.SYSTEM);
Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
Icon.createWithResource(r, R.drawable.ic_screenshot_share),
@@ -323,7 +326,7 @@
editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, 0,
- editIntent, 0, null, UserHandle.CURRENT);
+ editIntent, PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT);
// Make sure pending intents for the system user are still unique across users
// by setting the (otherwise unused) request code to the current user id.
@@ -338,7 +341,8 @@
mSmartActionsEnabled)
.setAction(Intent.ACTION_EDIT)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
- PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
+ UserHandle.SYSTEM);
Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
Icon.createWithResource(r, R.drawable.ic_screenshot_edit),
r.getString(com.android.internal.R.string.screenshot_edit), editAction);
@@ -360,7 +364,9 @@
.putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED,
mSmartActionsEnabled)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
+ PendingIntent.FLAG_CANCEL_CURRENT
+ | PendingIntent.FLAG_ONE_SHOT
+ | PendingIntent.FLAG_IMMUTABLE);
Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
Icon.createWithResource(r, R.drawable.ic_screenshot_delete),
r.getString(com.android.internal.R.string.delete), deleteAction);
@@ -401,7 +407,7 @@
PendingIntent broadcastIntent = PendingIntent.getBroadcast(context,
mRandom.nextInt(),
intent,
- PendingIntent.FLAG_CANCEL_CURRENT);
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
broadcastActions.add(new Notification.Action.Builder(action.getIcon(), action.title,
broadcastIntent).setContextual(true).addExtras(extras).build());
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
index a946513..db5a494 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
@@ -25,13 +25,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Picture;
import android.os.UserHandle;
import android.util.DisplayMetrics;
import android.view.WindowManager;
@@ -52,179 +45,16 @@
private final Context mContext;
private final Resources mResources;
private final NotificationManager mNotificationManager;
- private final Notification.BigPictureStyle mNotificationStyle;
-
- private int mIconSize;
- private int mPreviewWidth, mPreviewHeight;
- private Notification.Builder mNotificationBuilder, mPublicNotificationBuilder;
@Inject
ScreenshotNotificationsController(Context context, WindowManager windowManager) {
mContext = context;
mResources = context.getResources();
-
mNotificationManager =
(NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
- mIconSize = mResources.getDimensionPixelSize(
- android.R.dimen.notification_large_icon_height);
-
DisplayMetrics displayMetrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getRealMetrics(displayMetrics);
-
-
- // determine the optimal preview size
- int panelWidth = 0;
- try {
- panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
- } catch (Resources.NotFoundException e) {
- }
- if (panelWidth <= 0) {
- // includes notification_panel_width==match_parent (-1)
- panelWidth = displayMetrics.widthPixels;
- }
- mPreviewWidth = panelWidth;
- mPreviewHeight = mResources.getDimensionPixelSize(R.dimen.notification_max_height);
-
- // Setup the notification
- mNotificationStyle = new Notification.BigPictureStyle();
- }
-
- /**
- * Resets the notification builders.
- */
- public void reset() {
- // The public notification will show similar info but with the actual screenshot omitted
- mPublicNotificationBuilder =
- new Notification.Builder(mContext, NotificationChannels.SCREENSHOTS_HEADSUP);
- mNotificationBuilder =
- new Notification.Builder(mContext, NotificationChannels.SCREENSHOTS_HEADSUP);
- }
-
- /**
- * Sets the current screenshot bitmap.
- *
- * @param image the bitmap of the current screenshot (used for preview)
- */
- public void setImage(Bitmap image) {
- // Create the large notification icon
- int imageWidth = image.getWidth();
- int imageHeight = image.getHeight();
-
- Paint paint = new Paint();
- ColorMatrix desat = new ColorMatrix();
- desat.setSaturation(0.25f);
- paint.setColorFilter(new ColorMatrixColorFilter(desat));
- Matrix matrix = new Matrix();
- int overlayColor = 0x40FFFFFF;
-
- matrix.setTranslate((mPreviewWidth - imageWidth) / 2f, (mPreviewHeight - imageHeight) / 2f);
-
- Bitmap picture = generateAdjustedHwBitmap(
- image, mPreviewWidth, mPreviewHeight, matrix, paint, overlayColor);
-
- mNotificationStyle.bigPicture(picture.asShared());
-
- // Note, we can't use the preview for the small icon, since it is non-square
- float scale = (float) mIconSize / Math.min(imageWidth, imageHeight);
- matrix.setScale(scale, scale);
- matrix.postTranslate(
- (mIconSize - (scale * imageWidth)) / 2,
- (mIconSize - (scale * imageHeight)) / 2);
- Bitmap icon =
- generateAdjustedHwBitmap(image, mIconSize, mIconSize, matrix, paint, overlayColor);
-
- /**
- * NOTE: The following code prepares the notification builder for updating the
- * notification after the screenshot has been written to disk.
- */
-
- // On the tablet, the large icon makes the notification appear as if it is clickable
- // (and on small devices, the large icon is not shown) so defer showing the large icon
- // until we compose the final post-save notification below.
- mNotificationBuilder.setLargeIcon(icon.asShared());
- // But we still don't set it for the expanded view, allowing the smallIcon to show here.
- mNotificationStyle.bigLargeIcon((Bitmap) null);
- }
-
- /**
- * Shows a notification to inform the user that a screenshot is currently being saved.
- */
- public void showSavingScreenshotNotification() {
- final long now = System.currentTimeMillis();
-
- mPublicNotificationBuilder
- .setContentTitle(mResources.getString(R.string.screenshot_saving_title))
- .setSmallIcon(R.drawable.stat_notify_image)
- .setCategory(Notification.CATEGORY_PROGRESS)
- .setWhen(now)
- .setShowWhen(true)
- .setColor(mResources.getColor(
- com.android.internal.R.color.system_notification_accent_color));
- SystemUI.overrideNotificationAppName(mContext, mPublicNotificationBuilder, true);
-
- mNotificationBuilder
- .setContentTitle(mResources.getString(R.string.screenshot_saving_title))
- .setSmallIcon(R.drawable.stat_notify_image)
- .setWhen(now)
- .setShowWhen(true)
- .setColor(mResources.getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setStyle(mNotificationStyle)
- .setPublicVersion(mPublicNotificationBuilder.build());
- mNotificationBuilder.setFlag(Notification.FLAG_NO_CLEAR, true);
- SystemUI.overrideNotificationAppName(mContext, mNotificationBuilder, true);
-
- mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT,
- mNotificationBuilder.build());
- }
-
- /**
- * Shows a notification with the saved screenshot and actions that can be taken with it.
- *
- * @param actionData SavedImageData struct with image URI and actions
- */
- public void showScreenshotActionsNotification(
- ScreenshotController.SavedImageData actionData) {
- mNotificationBuilder.addAction(actionData.shareAction);
- mNotificationBuilder.addAction(actionData.editAction);
- mNotificationBuilder.addAction(actionData.deleteAction);
- for (Notification.Action smartAction : actionData.smartActions) {
- mNotificationBuilder.addAction(smartAction);
- }
-
- // Create the intent to show the screenshot in gallery
- Intent launchIntent = new Intent(Intent.ACTION_VIEW);
- launchIntent.setDataAndType(actionData.uri, "image/png");
- launchIntent.setFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
- final long now = System.currentTimeMillis();
-
- // Update the text and the icon for the existing notification
- mPublicNotificationBuilder
- .setContentTitle(mResources.getString(R.string.screenshot_saved_title))
- .setContentText(mResources.getString(R.string.screenshot_saved_text))
- .setContentIntent(PendingIntent
- .getActivity(mContext, 0, launchIntent, PendingIntent.FLAG_IMMUTABLE))
- .setWhen(now)
- .setAutoCancel(true)
- .setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color));
- mNotificationBuilder
- .setContentTitle(mResources.getString(R.string.screenshot_saved_title))
- .setContentText(mResources.getString(R.string.screenshot_saved_text))
- .setContentIntent(PendingIntent
- .getActivity(mContext, 0, launchIntent, PendingIntent.FLAG_IMMUTABLE))
- .setWhen(now)
- .setAutoCancel(true)
- .setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setPublicVersion(mPublicNotificationBuilder.build())
- .setFlag(Notification.FLAG_NO_CLEAR, false);
-
- mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT,
- mNotificationBuilder.build());
}
/**
@@ -263,31 +93,4 @@
.build();
mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT, n);
}
-
- /**
- * Cancels the current screenshot notification.
- */
- public void cancelNotification() {
- mNotificationManager.cancel(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT);
- }
-
- /**
- * Generates a new hardware bitmap with specified values, copying the content from the
- * passed in bitmap.
- */
- private Bitmap generateAdjustedHwBitmap(Bitmap bitmap, int width, int height, Matrix matrix,
- Paint paint, int color) {
- Picture picture = new Picture();
- Canvas canvas = picture.beginRecording(width, height);
- canvas.drawColor(color);
- canvas.drawBitmap(bitmap, matrix, paint);
- picture.endRecording();
- return Bitmap.createBitmap(picture);
- }
-
- static void cancelScreenshotNotification(Context context) {
- final NotificationManager nm =
- (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
- nm.cancel(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index 5976582..25c8e7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -170,7 +170,7 @@
private void sanitizeChild(View child) {
if (child != null) {
ViewGroup header = child.findViewById(
- com.android.internal.R.id.notification_header);
+ com.android.internal.R.id.notification_top_line);
sanitizeHeader(header);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 8d82270..3c48302 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -54,7 +54,6 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -345,8 +344,7 @@
return false;
}
boolean exceedsPriorityThreshold;
- if (NotificationUtils.useNewInterruptionModel(mContext)
- && hideSilentNotificationsOnLockscreen()) {
+ if (hideSilentNotificationsOnLockscreen()) {
exceedsPriorityThreshold =
entry.getBucket() == BUCKET_MEDIA_CONTROLS
|| (entry.getBucket() != BUCKET_SILENT
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java
index 5794f73..c301002 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java
@@ -56,7 +56,7 @@
try {
channel.setName(getName(entry));
notificationManager.createConversationNotificationChannelForPackage(
- pkg, appUid, entry.getSbn().getKey(), channel,
+ pkg, appUid, channel,
conversationId);
channel = notificationManager.getConversationNotificationChannel(
context.getOpPackageName(), UserHandle.getUserId(appUid), pkg,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
index ce6013f..cd8897e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
@@ -61,10 +61,8 @@
isFilteringEnabled() && !isMediaControlsEnabled() ->
intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_PEOPLE,
BUCKET_ALERTING, BUCKET_SILENT)
- NotificationUtils.useNewInterruptionModel(context) ->
- intArrayOf(BUCKET_ALERTING, BUCKET_SILENT)
else ->
- intArrayOf(BUCKET_ALERTING)
+ intArrayOf(BUCKET_ALERTING, BUCKET_SILENT)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index 1af47dd..cbc113b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -16,12 +16,9 @@
package com.android.systemui.statusbar.notification;
-import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL;
-
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Color;
-import android.provider.Settings;
import android.view.View;
import android.widget.ImageView;
@@ -76,15 +73,4 @@
return (int) (dimensionPixelSize * factor);
}
- /**
- * Returns the value of the new interruption model setting. This result is cached and cannot
- * change except through reboots/process restarts.
- */
- public static boolean useNewInterruptionModel(Context context) {
- if (sUseNewInterruptionModel == null) {
- sUseNewInterruptionModel = Settings.Secure.getInt(context.getContentResolver(),
- NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) != 0;
- }
- return sUseNewInterruptionModel;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index 318cdb1..b1c6f53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -37,7 +37,6 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -150,8 +149,7 @@
if (entry == null) {
return false;
}
- if (NotificationUtils.useNewInterruptionModel(mContext)
- && mHideSilentNotificationsOnLockscreen) {
+ if (mHideSilentNotificationsOnLockscreen) {
return mHighPriorityProvider.isHighPriority(entry);
} else {
return entry.getRepresentativeEntry() != null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index f788dfe..5ee66fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1643,19 +1643,15 @@
/** Sets the last time the notification being displayed audibly alerted the user. */
public void setLastAudiblyAlertedMs(long lastAudiblyAlertedMs) {
- if (NotificationUtils.useNewInterruptionModel(mContext)) {
- long timeSinceAlertedAudibly = System.currentTimeMillis() - lastAudiblyAlertedMs;
- boolean alertedRecently =
- timeSinceAlertedAudibly < RECENTLY_ALERTED_THRESHOLD_MS;
+ long timeSinceAlertedAudibly = System.currentTimeMillis() - lastAudiblyAlertedMs;
+ boolean alertedRecently = timeSinceAlertedAudibly < RECENTLY_ALERTED_THRESHOLD_MS;
- applyAudiblyAlertedRecently(alertedRecently);
+ applyAudiblyAlertedRecently(alertedRecently);
- removeCallbacks(mExpireRecentlyAlertedFlag);
- if (alertedRecently) {
- long timeUntilNoLongerRecent =
- RECENTLY_ALERTED_THRESHOLD_MS - timeSinceAlertedAudibly;
- postDelayed(mExpireRecentlyAlertedFlag, timeUntilNoLongerRecent);
- }
+ removeCallbacks(mExpireRecentlyAlertedFlag);
+ if (alertedRecently) {
+ long timeUntilNoLongerRecent = RECENTLY_ALERTED_THRESHOLD_MS - timeSinceAlertedAudibly;
+ postDelayed(mExpireRecentlyAlertedFlag, timeUntilNoLongerRecent);
}
}
@@ -2856,7 +2852,8 @@
}
float x = event.getX();
float y = event.getY();
- NotificationHeaderView header = getVisibleNotificationViewWrapper().getNotificationHeader();
+ NotificationViewWrapper wrapper = getVisibleNotificationViewWrapper();
+ NotificationHeaderView header = wrapper == null ? null : wrapper.getNotificationHeader();
if (header != null && header.isInTouchRect(x - getTranslation(), y)) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 160b6f7..71e1d12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -336,7 +336,6 @@
? contractedHeader.getPaddingLeft()
: paddingEnd,
contractedHeader.getPaddingBottom());
- contractedHeader.setShowWorkBadgeAtEnd(false);
return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 8c82756..5aeacab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -22,8 +22,10 @@
import android.content.Context;
import android.util.ArraySet;
import android.view.NotificationHeaderView;
+import android.view.NotificationTopLineView;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewGroup.MarginLayoutParams;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
@@ -34,19 +36,17 @@
import com.android.internal.widget.NotificationExpandButton;
import com.android.settingslib.Utils;
import com.android.systemui.Interpolators;
-import com.android.systemui.R;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.ViewTransformationHelper;
import com.android.systemui.statusbar.notification.CustomInterpolatorTransformation;
import com.android.systemui.statusbar.notification.ImageTransformState;
-import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.TransformState;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import java.util.Stack;
/**
- * Wraps a notification header view.
+ * Wraps a notification view which may or may not include a header.
*/
public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
@@ -58,6 +58,7 @@
private CachingIconView mIcon;
private NotificationExpandButton mExpandButton;
protected NotificationHeaderView mNotificationHeader;
+ protected NotificationTopLineView mNotificationTopLine;
private TextView mHeaderText;
private TextView mAppNameText;
private ImageView mWorkProfileImage;
@@ -67,13 +68,9 @@
private boolean mIsLowPriority;
private boolean mTransformLowPriorityTitle;
- private boolean mShowExpandButtonAtEnd;
protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
super(ctx, view, row);
- mShowExpandButtonAtEnd = ctx.getResources().getBoolean(
- R.bool.config_showNotificationExpandButtonAtEnd)
- || NotificationUtils.useNewInterruptionModel(ctx);
mTransformationHelper = new ViewTransformationHelper();
// we want to avoid that the header clashes with the other text when transforming
@@ -113,17 +110,15 @@
mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header);
+ mNotificationTopLine = mView.findViewById(com.android.internal.R.id.notification_top_line);
mAudiblyAlertedIcon = mView.findViewById(com.android.internal.R.id.alerted_icon);
mFeedbackIcon = mView.findViewById(com.android.internal.R.id.feedback);
- if (mNotificationHeader != null) {
- mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd);
- }
}
private void addFeedbackOnClickListener(ExpandableNotificationRow row) {
View.OnClickListener listener = row.getFeedbackOnClickListener();
- if (mNotificationHeader != null) {
- mNotificationHeader.setFeedbackOnClickListener(listener);
+ if (mNotificationTopLine != null) {
+ mNotificationTopLine.setFeedbackOnClickListener(listener);
}
if (mFeedbackIcon != null) {
mFeedbackIcon.setOnClickListener(listener);
@@ -167,13 +162,11 @@
mAppNameText.setTextAppearance(
com.android.internal.R.style
.TextAppearance_DeviceDefault_Notification_Conversation_AppName);
- ViewGroup.MarginLayoutParams layoutParams =
- (ViewGroup.MarginLayoutParams) mAppNameText.getLayoutParams();
+ MarginLayoutParams layoutParams = (MarginLayoutParams) mAppNameText.getLayoutParams();
layoutParams.setMarginStart(0);
}
if (mIconContainer != null) {
- ViewGroup.MarginLayoutParams layoutParams =
- (ViewGroup.MarginLayoutParams) mIconContainer.getLayoutParams();
+ MarginLayoutParams layoutParams = (MarginLayoutParams) mIconContainer.getLayoutParams();
layoutParams.width =
mIconContainer.getContext().getResources().getDimensionPixelSize(
com.android.internal.R.dimen.conversation_content_start);
@@ -183,8 +176,7 @@
layoutParams.setMarginStart(marginStart * -1);
}
if (mIcon != null) {
- ViewGroup.MarginLayoutParams layoutParams =
- (ViewGroup.MarginLayoutParams) mIcon.getLayoutParams();
+ MarginLayoutParams layoutParams = (MarginLayoutParams) mIcon.getLayoutParams();
layoutParams.setMarginEnd(0);
}
}
@@ -196,21 +188,18 @@
com.android.internal.R.attr.notificationHeaderTextAppearance,
com.android.internal.R.style.TextAppearance_DeviceDefault_Notification_Info);
mAppNameText.setTextAppearance(textAppearance);
- ViewGroup.MarginLayoutParams layoutParams =
- (ViewGroup.MarginLayoutParams) mAppNameText.getLayoutParams();
+ MarginLayoutParams layoutParams = (MarginLayoutParams) mAppNameText.getLayoutParams();
final int marginStart = mAppNameText.getContext().getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_header_app_name_margin_start);
layoutParams.setMarginStart(marginStart);
}
if (mIconContainer != null) {
- ViewGroup.MarginLayoutParams layoutParams =
- (ViewGroup.MarginLayoutParams) mIconContainer.getLayoutParams();
+ MarginLayoutParams layoutParams = (MarginLayoutParams) mIconContainer.getLayoutParams();
layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
layoutParams.setMarginStart(0);
}
if (mIcon != null) {
- ViewGroup.MarginLayoutParams layoutParams =
- (ViewGroup.MarginLayoutParams) mIcon.getLayoutParams();
+ MarginLayoutParams layoutParams = (MarginLayoutParams) mIcon.getLayoutParams();
final int marginEnd = mIcon.getContext().getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_header_icon_margin_end);
layoutParams.setMarginEnd(marginEnd);
@@ -270,6 +259,7 @@
@Override
public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
+ mExpandButton.setOnClickListener(expandable ? onClickListener : null);
if (mNotificationHeader != null) {
mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 9320499..5cee5bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -1340,8 +1340,11 @@
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
private float getAppearStartPosition() {
if (isHeadsUpTransition()) {
- return mHeadsUpInset
- + getFirstVisibleSection().getFirstVisibleChild().getPinnedHeadsUpHeight();
+ final NotificationSection firstVisibleSection = getFirstVisibleSection();
+ final int pinnedHeight = firstVisibleSection != null
+ ? firstVisibleSection.getFirstVisibleChild().getPinnedHeadsUpHeight()
+ : 0;
+ return mHeadsUpInset + pinnedHeight;
}
return getMinExpansionHeight();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index d1c8355..e065b47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -91,9 +91,7 @@
private boolean mAnimationsEnabled;
private int mAodIconTint;
- private boolean mFullyHidden;
private boolean mAodIconsVisible;
- private boolean mIsPulsing;
private boolean mShowLowPriority = true;
@VisibleForTesting
@@ -158,22 +156,23 @@
}
/**
- * Called by the StatusBar. The StatusBar passes the NotificationIconContainer which holds
- * the aod icons.
+ * Called by the Keyguard*ViewController whose view contains the aod icons.
*/
- void setupAodIcons(@NonNull NotificationIconContainer aodIcons) {
+ public void setupAodIcons(@NonNull NotificationIconContainer aodIcons,
+ int lockScreenMode) {
boolean changed = mAodIcons != null;
if (changed) {
mAodIcons.setAnimationsEnabled(false);
mAodIcons.removeAllViews();
}
mAodIcons = aodIcons;
- mAodIcons.setOnLockScreen(true);
+ mAodIcons.setOnLockScreen(true, lockScreenMode);
updateAodIconsVisibility(false /* animate */);
updateAnimations();
if (changed) {
updateAodNotificationIcons();
}
+ updateIconLayoutParams(mContext);
}
public void setupShelf(NotificationShelfController notificationShelfController) {
@@ -182,23 +181,31 @@
}
public void onDensityOrFontScaleChanged(Context context) {
+ updateIconLayoutParams(context);
+ }
+
+ private void updateIconLayoutParams(Context context) {
reloadDimens(context);
final FrameLayout.LayoutParams params = generateIconLayoutParams();
for (int i = 0; i < mNotificationIcons.getChildCount(); i++) {
View child = mNotificationIcons.getChildAt(i);
child.setLayoutParams(params);
}
- for (int i = 0; i < mShelfIcons.getChildCount(); i++) {
- View child = mShelfIcons.getChildAt(i);
- child.setLayoutParams(params);
- }
for (int i = 0; i < mCenteredIcon.getChildCount(); i++) {
View child = mCenteredIcon.getChildAt(i);
child.setLayoutParams(params);
}
- for (int i = 0; i < mAodIcons.getChildCount(); i++) {
- View child = mAodIcons.getChildAt(i);
- child.setLayoutParams(params);
+ if (mShelfIcons != null) {
+ for (int i = 0; i < mShelfIcons.getChildCount(); i++) {
+ View child = mShelfIcons.getChildAt(i);
+ child.setLayoutParams(params);
+ }
+ }
+ if (mAodIcons != null) {
+ for (int i = 0; i < mAodIcons.getChildCount(); i++) {
+ View child = mAodIcons.getChildAt(i);
+ child.setLayoutParams(params);
+ }
}
}
@@ -358,6 +365,9 @@
}
public void updateAodNotificationIcons() {
+ if (mAodIcons == null) {
+ return;
+ }
updateIconsForLayout(entry -> entry.getIcons().getAodIcon(), mAodIcons,
false /* showAmbient */,
true /* showLowPriority */,
@@ -546,6 +556,9 @@
@Override
public void onDozingChanged(boolean isDozing) {
+ if (mAodIcons == null) {
+ return;
+ }
boolean animate = mDozeParameters.getAlwaysOn()
&& !mDozeParameters.getDisplayNeedsBlanking();
mAodIcons.setDozing(isDozing, animate, 0);
@@ -564,7 +577,9 @@
private void updateAnimations() {
boolean inShade = mStatusBarStateController.getState() == StatusBarState.SHADE;
- mAodIcons.setAnimationsEnabled(mAnimationsEnabled && !inShade);
+ if (mAodIcons != null) {
+ mAodIcons.setAnimationsEnabled(mAnimationsEnabled && !inShade);
+ }
mCenteredIcon.setAnimationsEnabled(mAnimationsEnabled && inShade);
mNotificationIcons.setAnimationsEnabled(mAnimationsEnabled && inShade);
}
@@ -575,6 +590,9 @@
}
public void appearAodIcons() {
+ if (mAodIcons == null) {
+ return;
+ }
if (mDozeParameters.shouldControlScreenOff()) {
mAodIcons.setTranslationY(-mAodIconAppearTranslation);
mAodIcons.setAlpha(0);
@@ -637,6 +655,9 @@
}
private void updateAodIconsVisibility(boolean animate) {
+ if (mAodIcons == null) {
+ return;
+ }
boolean visible = mBypassController.getBypassEnabled()
|| mWakeUpCoordinator.getNotificationsFullyHidden();
if (mStatusBarStateController.getState() != StatusBarState.KEYGUARD) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index bf85852..9561851 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -34,6 +34,7 @@
import androidx.collection.ArrayMap;
import com.android.internal.statusbar.StatusBarIcon;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.AlphaOptimizedFrameLayout;
@@ -148,6 +149,7 @@
private float mActualPaddingStart = NO_VALUE;
private boolean mDozing;
private boolean mOnLockScreen;
+ private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
private boolean mChangingViewPositions;
private int mAddAnimationStartIndex = -1;
private int mCannedAnimationStartIndex = -1;
@@ -453,7 +455,8 @@
mFirstVisibleIconState = mIconStates.get(getChildAt(0));
}
- boolean center = mOnLockScreen;
+ boolean center = mOnLockScreen
+ && mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
if (center && translationX < getLayoutEnd()) {
float initialTranslation =
mFirstVisibleIconState == null ? 0 : mFirstVisibleIconState.xTranslation;
@@ -686,8 +689,13 @@
}
}
- public void setOnLockScreen(boolean onLockScreen) {
+ /**
+ * Set whether the device is on the lockscreen and which lockscreen mode the device is
+ * configured to. Depending on these values, the layout of the AOD icons change.
+ */
+ public void setOnLockScreen(boolean onLockScreen, int lockScreenMode) {
mOnLockScreen = onLockScreen;
+ mLockScreenMode = lockScreenMode;
}
public class IconState extends ViewState {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 47b16c8..b185f48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -441,7 +441,6 @@
private KeyguardIndicationController mKeyguardIndicationController;
private Consumer<Boolean> mAffordanceLaunchListener;
private int mShelfHeight;
- private Runnable mOnReinflationListener;
private int mDarkIconSize;
private int mHeadsUpInset;
private boolean mHeadsUpPinnedMode;
@@ -767,9 +766,6 @@
false,
mBarState);
setKeyguardBottomAreaVisibility(mBarState, false);
- if (mOnReinflationListener != null) {
- mOnReinflationListener.run();
- }
}
private void initBottomArea() {
@@ -3102,10 +3098,6 @@
mKeyguardIndicationController.showTransientIndication(id);
}
- public void setOnReinflationListener(Runnable onReinflationListener) {
- mOnReinflationListener = onReinflationListener;
- }
-
public void setAlpha(float alpha) {
mView.setAlpha(alpha);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 6ed092f..d537241 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -50,6 +50,7 @@
import com.android.systemui.privacy.PrivacyItem;
import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.privacy.PrivacyType;
+import com.android.systemui.privacy.logging.PrivacyLogger;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.screenrecord.RecordingController;
@@ -145,6 +146,7 @@
private final SensorPrivacyController mSensorPrivacyController;
private final RecordingController mRecordingController;
private final RingerModeTracker mRingerModeTracker;
+ private final PrivacyLogger mPrivacyLogger;
private boolean mZenVisible;
private boolean mVolumeVisible;
@@ -172,7 +174,8 @@
@Nullable TelecomManager telecomManager, @DisplayId int displayId,
@Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil,
RingerModeTracker ringerModeTracker,
- PrivacyItemController privacyItemController) {
+ PrivacyItemController privacyItemController,
+ PrivacyLogger privacyLogger) {
mIconController = iconController;
mCommandQueue = commandQueue;
mBroadcastDispatcher = broadcastDispatcher;
@@ -197,6 +200,7 @@
mUiBgExecutor = uiBgExecutor;
mTelecomManager = telecomManager;
mRingerModeTracker = ringerModeTracker;
+ mPrivacyLogger = privacyLogger;
mSlotCast = resources.getString(com.android.internal.R.string.status_bar_cast);
mSlotHotspot = resources.getString(com.android.internal.R.string.status_bar_hotspot);
@@ -675,6 +679,7 @@
|| mPrivacyItemController.getLocationAvailable()) {
mIconController.setIconVisibility(mSlotLocation, showLocation);
}
+ mPrivacyLogger.logStatusBarIconsVisible(showCamera, showMicrophone, showLocation);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index c7932bb..9bd98f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1035,10 +1035,8 @@
mStackScrollerController.getNotificationListContainer();
mNotificationLogger.setUpWithContainer(notifListContainer);
- updateAodIconArea();
inflateShelf();
mNotificationIconAreaController.setupShelf(mNotificationShelfController);
- mNotificationPanelViewController.setOnReinflationListener(this::updateAodIconArea);
mNotificationPanelViewController.addExpansionListener(mWakeUpCoordinator);
// Allow plugins to reference DarkIconDispatcher and StatusBarStateController
@@ -1270,12 +1268,6 @@
ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f));
}
- private void updateAodIconArea() {
- mNotificationIconAreaController.setupAodIcons(
- getNotificationShadeWindowView()
- .findViewById(R.id.clock_notification_icon_container));
- }
-
@NonNull
@Override
public Lifecycle getLifecycle() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index c84589a..4552026 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -73,9 +73,7 @@
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.LightBarController;
-import java.util.Collections;
import java.util.HashMap;
-import java.util.Set;
import java.util.function.Consumer;
/**
@@ -315,7 +313,8 @@
mRemoteInputs = remoteInputs;
mRemoteInput = remoteInput;
mEditText.setHint(mRemoteInput.getLabel());
- mEditText.mSupportedMimeTypes = remoteInput.getAllowedDataTypes();
+ mEditText.mSupportedMimeTypes = (remoteInput.getAllowedDataTypes() == null) ? null
+ : remoteInput.getAllowedDataTypes().toArray(new String[0]);
mEntry.editedSuggestionInfo = editedSuggestionInfo;
if (editedSuggestionInfo != null) {
@@ -574,7 +573,7 @@
boolean mShowImeOnInputConnection;
private LightBarController mLightBarController;
UserHandle mUser;
- private Set<String> mSupportedMimeTypes;
+ private String[] mSupportedMimeTypes;
public RemoteEditText(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -585,35 +584,31 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- setOnReceiveContentCallback(new OnReceiveContentCallback<View>() {
- @Override
- public boolean onReceiveContent(@NonNull View view, @NonNull Payload payload) {
- ClipData clip = payload.getClip();
- if (clip.getItemCount() == 0) {
- return false;
- }
- Uri contentUri = clip.getItemAt(0).getUri();
- ClipDescription description = clip.getDescription();
- String mimeType = null;
- if (description.getMimeTypeCount() > 0) {
- mimeType = description.getMimeType(0);
- }
- if (mimeType != null) {
- Intent dataIntent = mRemoteInputView
- .prepareRemoteInputFromData(mimeType, contentUri);
- mRemoteInputView.sendRemoteInput(dataIntent);
- }
- return true;
- }
-
- @NonNull
- @Override
- public Set<String> getSupportedMimeTypes(@NonNull View view) {
- return mSupportedMimeTypes != null
- ? mSupportedMimeTypes
- : Collections.emptySet();
- }
- });
+ if (mSupportedMimeTypes != null && mSupportedMimeTypes.length > 0) {
+ setOnReceiveContentCallback(mSupportedMimeTypes,
+ new OnReceiveContentCallback<View>() {
+ @Override
+ public boolean onReceiveContent(@NonNull View view,
+ @NonNull Payload payload) {
+ ClipData clip = payload.getClip();
+ if (clip.getItemCount() == 0) {
+ return false;
+ }
+ Uri contentUri = clip.getItemAt(0).getUri();
+ ClipDescription description = clip.getDescription();
+ String mimeType = null;
+ if (description.getMimeTypeCount() > 0) {
+ mimeType = description.getMimeType(0);
+ }
+ if (mimeType != null) {
+ Intent dataIntent = mRemoteInputView
+ .prepareRemoteInputFromData(mimeType, contentUri);
+ mRemoteInputView.sendRemoteInput(dataIntent);
+ }
+ return true;
+ }
+ });
+ }
}
private void defocusIfNeeded(boolean animate) {
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
index 1c682e3..409d136 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -129,6 +129,8 @@
mCallback = callback;
mPresenter = new ToastPresenter(context, mIAccessibilityManager, mNotificationManager,
packageName);
+ // Set as trusted overlay so touches can pass through toasts
+ mPresenter.getLayoutParams().setTrustedOverlay();
mToastLogger.logOnShowToast(uid, packageName, text.toString(), token.toString());
mPresenter.show(mToast.getView(), token, windowToken, duration, mToast.getGravity(),
mToast.getXOffset(), mToast.getYOffset(), mToast.getHorizontalMargin(),
diff --git a/packages/SystemUI/src/com/android/systemui/util/ViewController.java b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
index 3dbc6f1..880d09a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
@@ -26,8 +26,8 @@
*
* Implementations should handle setup and teardown related activities inside of
* {@link #onViewAttached()} and {@link #onViewDetached()}. Be sure to call {@link #init()} on
- * any child controllers that this uses. This can be done in {@link init()} if the controllers
- * are injected, or right after creation time of the child controller.
+ * any child controllers that this uses. This can be done in {@link #onInit()} if the
+ * controllers are injected, or right after creation time of the child controller.
*
* Tip: View "attachment" happens top down - parents are notified that they are attached before
* any children. That means that if you call a method on a child controller in
@@ -62,11 +62,18 @@
mView = view;
}
- /** Call immediately after constructing Controller in order to handle view lifecycle events. */
+ /**
+ * Call immediately after constructing Controller in order to handle view lifecycle events.
+ *
+ * Generally speaking, you don't want to override this method. Instead, override
+ * {@link #onInit()} as a way to have an run-once idempotent method that you can use for
+ * setup of your ViewController.
+ */
public void init() {
if (mInited) {
return;
}
+ onInit();
mInited = true;
if (mView != null) {
@@ -77,6 +84,14 @@
}
}
+ /**
+ * Run once when {@link #init()} is called.
+ *
+ * Override this to perform idempotent, one-time setup that your controller needs. It will
+ * be called before {@link #onViewAttached()}.
+ */
+ protected void onInit() {}
+
protected Context getContext() {
return mView.getContext();
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
index 5310b3f..6bedd39 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
@@ -50,12 +50,14 @@
@Provides
static Optional<Pip> providePip(
Context context,
+ PipBoundsState pipBoundsState,
PipBoundsHandler pipBoundsHandler,
PipTaskOrganizer pipTaskOrganizer,
WindowManagerShellWrapper windowManagerShellWrapper) {
return Optional.of(
new PipController(
context,
+ pipBoundsState,
pipBoundsHandler,
pipTaskOrganizer,
windowManagerShellWrapper));
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 89ea9e2..4330659 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -64,7 +64,7 @@
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tracing.nano.SystemUiTraceProto;
-import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellDump;
import com.android.wm.shell.nano.WmShellTraceProto;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedEvents;
@@ -106,13 +106,12 @@
private final NavigationModeController mNavigationModeController;
private final ScreenLifecycle mScreenLifecycle;
private final SysUiState mSysUiState;
- // TODO: This is only here because we need to dump state. Remove and replace with a dumper
- // interface.
- private final ShellTaskOrganizer mShellTaskOrganizer;
private final Optional<Pip> mPipOptional;
private final Optional<SplitScreen> mSplitScreenOptional;
private final Optional<OneHanded> mOneHandedOptional;
private final ProtoTracer mProtoTracer;
+ private final Optional<ShellDump> mShellDump;
+
private boolean mIsSysUiStateValid;
private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
@@ -130,8 +129,8 @@
Optional<Pip> pipOptional,
Optional<SplitScreen> splitScreenOptional,
Optional<OneHanded> oneHandedOptional,
- ShellTaskOrganizer shellTaskOrganizer,
- ProtoTracer protoTracer) {
+ ProtoTracer protoTracer,
+ Optional<ShellDump> shellDump) {
super(context);
mCommandQueue = commandQueue;
mConfigurationController = configurationController;
@@ -144,9 +143,9 @@
mPipOptional = pipOptional;
mSplitScreenOptional = splitScreenOptional;
mOneHandedOptional = oneHandedOptional;
- mShellTaskOrganizer = shellTaskOrganizer;
mProtoTracer = protoTracer;
mProtoTracer.add(this);
+ mShellDump = shellDump;
}
@Override
@@ -410,12 +409,7 @@
return;
}
// Dump WMShell stuff here if no commands were handled
- mShellTaskOrganizer.dump(pw, "");
- pw.println();
- pw.println();
- mPipOptional.ifPresent(pip -> pip.dump(pw));
- mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw));
- mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw));
+ mShellDump.ifPresent((shellDump) -> shellDump.dump(pw));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index b333f8b..cff1c9d 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -27,15 +27,20 @@
import com.android.systemui.dagger.WMSingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shared.system.InputConsumerController;
+import com.android.wm.shell.ShellDump;
+import com.android.wm.shell.ShellInit;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.AnimationThread;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.HandlerExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedController;
+import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.phone.PipAppOpsListener;
@@ -55,6 +60,33 @@
*/
@Module
public abstract class WMShellBaseModule {
+
+ @WMSingleton
+ @Provides
+ static ShellInit provideShellInit(DisplayImeController displayImeController,
+ DragAndDropController dragAndDropController,
+ ShellTaskOrganizer shellTaskOrganizer,
+ Optional<SplitScreen> splitScreenOptional) {
+ return new ShellInit(displayImeController,
+ dragAndDropController,
+ shellTaskOrganizer,
+ splitScreenOptional);
+ }
+
+ /**
+ * Note, this is only optional because we currently pass this to the SysUI component scope and
+ * for non-primary users, we may inject a null-optional for that dependency.
+ */
+ @WMSingleton
+ @Provides
+ static Optional<ShellDump> provideShellDump(ShellTaskOrganizer shellTaskOrganizer,
+ Optional<SplitScreen> splitScreenOptional,
+ Optional<Pip> pipOptional,
+ Optional<OneHanded> oneHandedOptional) {
+ return Optional.of(new ShellDump(shellTaskOrganizer, splitScreenOptional, pipOptional,
+ oneHandedOptional));
+ }
+
@WMSingleton
@Provides
static TransactionPool provideTransactionPool() {
@@ -70,6 +102,13 @@
@WMSingleton
@Provides
+ static DragAndDropController provideDragAndDropController(Context context,
+ DisplayController displayController) {
+ return new DragAndDropController(context, displayController);
+ }
+
+ @WMSingleton
+ @Provides
static InputConsumerController provideInputConsumerController() {
return InputConsumerController.getPipInputConsumer();
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 094230d..4559113 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -16,7 +16,6 @@
package com.android.keyguard;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
@@ -38,6 +37,8 @@
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.NotificationIconAreaController;
+import com.android.systemui.statusbar.phone.NotificationIconContainer;
import org.junit.Before;
import org.junit.Test;
@@ -60,6 +61,8 @@
@Mock
private KeyguardClockSwitch mView;
@Mock
+ private NotificationIconContainer mNotificationIcons;
+ @Mock
private ClockPlugin mClockPlugin;
@Mock
ColorExtractor.GradientColors mGradientColors;
@@ -67,6 +70,8 @@
KeyguardSliceViewController mKeyguardSliceViewController;
@Mock
Resources mResources;
+ @Mock
+ NotificationIconAreaController mNotificationIconAreaController;
private KeyguardClockSwitchController mController;
@@ -74,6 +79,8 @@
public void setup() {
MockitoAnnotations.initMocks(this);
+ when(mView.findViewById(com.android.systemui.R.id.left_aligned_notification_icon_container))
+ .thenReturn(mNotificationIcons);
when(mView.isAttachedToWindow()).thenReturn(true);
when(mResources.getString(anyInt())).thenReturn("h:mm");
mController = new KeyguardClockSwitchController(
@@ -82,7 +89,8 @@
mStatusBarStateController,
mColorExtractor,
mClockManager,
- mKeyguardSliceViewController);
+ mKeyguardSliceViewController,
+ mNotificationIconAreaController);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 752a744..4cfbe69 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -22,6 +22,7 @@
import android.testing.AndroidTestingRunner;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -47,6 +48,8 @@
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
ConfigurationController mConfigurationController;
+ @Mock
+ NotificationIconAreaController mNotificationIconAreaController;
private KeyguardStatusViewController mController;
@@ -60,7 +63,8 @@
mKeyguardClockSwitchController,
mKeyguardStateController,
mKeyguardUpdateMonitor,
- mConfigurationController);
+ mConfigurationController,
+ mNotificationIconAreaController);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index 3da1f29..c923515 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -43,6 +43,7 @@
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.os.UserHandle;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.view.MotionEvent;
@@ -186,8 +187,8 @@
// Perform dragging
final View.OnTouchListener listener = mTouchListenerCaptor.getValue();
final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop();
- final int previousMode = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
+ final int previousMode = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0, UserHandle.USER_CURRENT);
listener.onTouch(mSpyImageView, MotionEvent.obtain(
0, 0, ACTION_DOWN, 100, 100, 0));
verify(mViewPropertyAnimator).cancel();
@@ -334,8 +335,8 @@
verify(mSpyImageView).setImageResource(
getIconResId(expectedMode));
verify(mWindowManager).removeView(mSpyImageView);
- final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
+ final int actualMode = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0, UserHandle.USER_CURRENT);
assertEquals(expectedMode, actualMode);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
index da00e7e..1a78ca4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
@@ -53,7 +53,7 @@
receiver: BroadcastReceiver,
filter: IntentFilter,
executor: Executor?,
- user: UserHandle
+ user: UserHandle?
) {
registeredReceivers.add(receiver)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java
index b13c6fc..5c14859 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java
@@ -36,7 +36,6 @@
import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.content.Context;
-import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.SurfaceControl;
@@ -101,7 +100,8 @@
return null;
}).when(mExecutor).execute(any());
- mTaskView = new TaskView(mContext, mOrganizer, mExecutor);
+ mTaskView = new TaskView(mContext, mOrganizer);
+ mTaskView.setExecutor(mExecutor);
mTaskView.setListener(mViewListener);
}
@@ -114,7 +114,8 @@
@Test
public void testSetPendingListener_throwsException() {
- TaskView taskView = new TaskView(mContext, mOrganizer, mExecutor);
+ TaskView taskView = new TaskView(mContext, mOrganizer);
+ mTaskView.setExecutor(mExecutor);
taskView.setListener(mViewListener);
try {
taskView.setListener(mViewListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index 4f0ff42..0059881 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -42,6 +42,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.AuthController;
import com.android.systemui.doze.DozeSensors.TriggerSensor;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -84,6 +85,8 @@
@Mock
private DozeLog mDozeLog;
@Mock
+ private AuthController mAuthController;
+ @Mock
private ProximitySensor mProximitySensor;
private FakeSettings mFakeSettings = new FakeSettings();
private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
@@ -156,7 +159,7 @@
TestableDozeSensors() {
super(getContext(), mSensorManager, mDozeParameters,
mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
- mProximitySensor, mFakeSettings);
+ mProximitySensor, mFakeSettings, mAuthController);
for (TriggerSensor sensor : mSensors) {
if (sensor instanceof PluginSensor
&& ((PluginSensor) sensor).mPluginSensor.getType()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 3ae02a4..6d87cc3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -196,6 +196,7 @@
final int screenY = 100;
final int reason = DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS;
mTriggers.onSensor(reason, screenX, screenY, null);
+ verify(mHost).extendPulse(reason);
verify(mAuthController).onAodInterrupt(eq(screenX), eq(screenY));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 2d460aa..5218886 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -25,7 +25,7 @@
import android.graphics.drawable.Icon;
import android.testing.AndroidTestingRunner;
import android.view.View;
-import android.widget.FrameLayout;
+import android.widget.LinearLayout;
import androidx.core.graphics.drawable.IconCompat;
import androidx.test.filters.SmallTest;
@@ -66,7 +66,7 @@
public void setUp() {
mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
- .onCreateViewHolder(new FrameLayout(mContext), 0);
+ .onCreateViewHolder(new LinearLayout(mContext), 0);
when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices);
when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false);
@@ -75,6 +75,7 @@
when(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).thenReturn(mIconCompat);
when(mMediaOutputController.getDeviceIconCompat(mMediaDevice2)).thenReturn(mIconCompat);
when(mMediaOutputController.getCurrentConnectedMediaDevice()).thenReturn(mMediaDevice1);
+ when(mMediaOutputController.isActiveRemoteDevice(mMediaDevice1)).thenReturn(true);
when(mIconCompat.toIcon(mContext)).thenReturn(mIcon);
when(mMediaDevice1.getName()).thenReturn(TEST_DEVICE_NAME_1);
when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_ID_1);
@@ -107,6 +108,11 @@
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getText()).isEqualTo(mContext.getText(
R.string.media_output_dialog_pairing_new));
}
@@ -118,19 +124,41 @@
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
}
@Test
+ public void onBindViewHolder_bindNonActiveConnectedDevice_verifyView() {
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+
+ assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
+ }
+
+ @Test
public void onBindViewHolder_bindDisconnectedBluetoothDevice_verifyView() {
when(mMediaDevice2.getDeviceType()).thenReturn(
MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
when(mMediaDevice2.isConnected()).thenReturn(false);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+ assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(
mContext.getString(R.string.media_output_dialog_disconnected, TEST_DEVICE_NAME_2));
@@ -142,9 +170,13 @@
LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+ assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mSubTitleText.getText()).isEqualTo(mContext.getText(
@@ -162,7 +194,11 @@
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
}
@@ -174,8 +210,13 @@
LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
- assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
}
@@ -183,7 +224,7 @@
public void onItemClick_clickPairNew_verifyLaunchBluetoothPairing() {
when(mMediaOutputController.isZeroMode()).thenReturn(true);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
- mViewHolder.mFrameLayout.performClick();
+ mViewHolder.mContainerLayout.performClick();
verify(mMediaOutputController).launchBluetoothPairing();
}
@@ -194,7 +235,7 @@
LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
- mViewHolder.mFrameLayout.performClick();
+ mViewHolder.mContainerLayout.performClick();
verify(mMediaOutputController).connectDevice(mMediaDevice2);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 27b5b7f..c897d8a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -39,6 +39,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
import org.junit.Before;
@@ -58,6 +59,8 @@
private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
private ShadeController mShadeController = mock(ShadeController.class);
private ActivityStarter mStarter = mock(ActivityStarter.class);
+ private NotificationEntryManager mNotificationEntryManager =
+ mock(NotificationEntryManager.class);
private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl;
private MediaOutputController mMediaOutputController;
@@ -68,8 +71,9 @@
@Before
public void setUp() {
- mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
- mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
+ mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
+ mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+ mNotificationEntryManager);
mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext,
mMediaOutputController);
mMediaOutputBaseDialogImpl.onCreate(new Bundle());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 0dcdecf..6ceac13 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -27,14 +27,19 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.Notification;
import android.content.Context;
+import android.graphics.drawable.Icon;
import android.media.MediaDescription;
import android.media.MediaMetadata;
import android.media.RoutingSessionInfo;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
+import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
+import android.text.TextUtils;
+import androidx.core.graphics.drawable.IconCompat;
import androidx.test.filters.SmallTest;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
@@ -44,6 +49,8 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.ShadeController;
import org.junit.Before;
@@ -60,6 +67,9 @@
private static final String TEST_PACKAGE_NAME = "com.test.package.name";
private static final String TEST_DEVICE_1_ID = "test_device_1_id";
private static final String TEST_DEVICE_2_ID = "test_device_2_id";
+ private static final String TEST_DEVICE_3_ID = "test_device_3_id";
+ private static final String TEST_DEVICE_4_ID = "test_device_4_id";
+ private static final String TEST_DEVICE_5_ID = "test_device_5_id";
private static final String TEST_ARTIST = "test_artist";
private static final String TEST_SONG = "test_song";
private static final String TEST_SESSION_ID = "test_session_id";
@@ -77,6 +87,8 @@
private RoutingSessionInfo mRemoteSessionInfo = mock(RoutingSessionInfo.class);
private ShadeController mShadeController = mock(ShadeController.class);
private ActivityStarter mStarter = mock(ActivityStarter.class);
+ private NotificationEntryManager mNotificationEntryManager =
+ mock(NotificationEntryManager.class);
private Context mSpyContext;
private MediaOutputController mMediaOutputController;
@@ -96,8 +108,10 @@
MediaSessionManager.class);
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(
mCachedBluetoothDeviceManager);
- mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME,
- mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
+
+ mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME, false,
+ mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+ mNotificationEntryManager);
mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
MediaDescription.Builder builder = new MediaDescription.Builder();
@@ -139,8 +153,9 @@
@Test
public void start_withoutPackageName_verifyMediaControllerInit() {
- mMediaOutputController = new MediaOutputController(mSpyContext, null, mMediaSessionManager,
- mLocalBluetoothManager, mShadeController, mStarter);
+ mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
+ mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+ mNotificationEntryManager);
mMediaOutputController.start(mCb);
@@ -159,8 +174,10 @@
@Test
public void stop_withoutPackageName_verifyMediaControllerDeinit() {
- mMediaOutputController = new MediaOutputController(mSpyContext, null, mMediaSessionManager,
- mLocalBluetoothManager, mShadeController, mStarter);
+ mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
+ mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+ mNotificationEntryManager);
+
mMediaOutputController.start(mCb);
mMediaOutputController.stop();
@@ -345,4 +362,127 @@
assertThat(mMediaOutputController.isZeroMode()).isFalse();
}
+
+ @Test
+ public void getGroupMediaDevices_differentDeviceOrder_showingSameOrder() {
+ final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class);
+ final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class);
+ final MediaDevice selectableMediaDevice1 = mock(MediaDevice.class);
+ final MediaDevice selectableMediaDevice2 = mock(MediaDevice.class);
+ final List<MediaDevice> selectedMediaDevices = new ArrayList<>();
+ final List<MediaDevice> selectableMediaDevices = new ArrayList<>();
+ when(selectedMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID);
+ when(selectedMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID);
+ when(selectableMediaDevice1.getId()).thenReturn(TEST_DEVICE_3_ID);
+ when(selectableMediaDevice2.getId()).thenReturn(TEST_DEVICE_4_ID);
+ selectedMediaDevices.add(selectedMediaDevice1);
+ selectedMediaDevices.add(selectedMediaDevice2);
+ selectableMediaDevices.add(selectableMediaDevice1);
+ selectableMediaDevices.add(selectableMediaDevice2);
+ doReturn(selectedMediaDevices).when(mLocalMediaManager).getSelectedMediaDevice();
+ doReturn(selectableMediaDevices).when(mLocalMediaManager).getSelectableMediaDevice();
+ final List<MediaDevice> groupMediaDevices = mMediaOutputController.getGroupMediaDevices();
+ // Reset order
+ selectedMediaDevices.clear();
+ selectedMediaDevices.add(selectedMediaDevice2);
+ selectedMediaDevices.add(selectedMediaDevice1);
+ selectableMediaDevices.clear();
+ selectableMediaDevices.add(selectableMediaDevice2);
+ selectableMediaDevices.add(selectableMediaDevice1);
+ final List<MediaDevice> newDevices = mMediaOutputController.getGroupMediaDevices();
+
+ assertThat(newDevices.size()).isEqualTo(groupMediaDevices.size());
+ for (int i = 0; i < groupMediaDevices.size(); i++) {
+ assertThat(TextUtils.equals(groupMediaDevices.get(i).getId(),
+ newDevices.get(i).getId())).isTrue();
+ }
+ }
+
+ @Test
+ public void getGroupMediaDevices_newDevice_verifyDeviceOrder() {
+ final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class);
+ final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class);
+ final MediaDevice selectableMediaDevice1 = mock(MediaDevice.class);
+ final MediaDevice selectableMediaDevice2 = mock(MediaDevice.class);
+ final MediaDevice selectableMediaDevice3 = mock(MediaDevice.class);
+ final List<MediaDevice> selectedMediaDevices = new ArrayList<>();
+ final List<MediaDevice> selectableMediaDevices = new ArrayList<>();
+ when(selectedMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID);
+ when(selectedMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID);
+ when(selectableMediaDevice1.getId()).thenReturn(TEST_DEVICE_3_ID);
+ when(selectableMediaDevice2.getId()).thenReturn(TEST_DEVICE_4_ID);
+ when(selectableMediaDevice3.getId()).thenReturn(TEST_DEVICE_5_ID);
+ selectedMediaDevices.add(selectedMediaDevice1);
+ selectedMediaDevices.add(selectedMediaDevice2);
+ selectableMediaDevices.add(selectableMediaDevice1);
+ selectableMediaDevices.add(selectableMediaDevice2);
+ doReturn(selectedMediaDevices).when(mLocalMediaManager).getSelectedMediaDevice();
+ doReturn(selectableMediaDevices).when(mLocalMediaManager).getSelectableMediaDevice();
+ final List<MediaDevice> groupMediaDevices = mMediaOutputController.getGroupMediaDevices();
+ // Reset order
+ selectedMediaDevices.clear();
+ selectedMediaDevices.add(selectedMediaDevice2);
+ selectedMediaDevices.add(selectedMediaDevice1);
+ selectableMediaDevices.clear();
+ selectableMediaDevices.add(selectableMediaDevice3);
+ selectableMediaDevices.add(selectableMediaDevice2);
+ selectableMediaDevices.add(selectableMediaDevice1);
+ final List<MediaDevice> newDevices = mMediaOutputController.getGroupMediaDevices();
+
+ assertThat(newDevices.size()).isEqualTo(5);
+ for (int i = 0; i < groupMediaDevices.size(); i++) {
+ assertThat(TextUtils.equals(groupMediaDevices.get(i).getId(),
+ newDevices.get(i).getId())).isTrue();
+ }
+ assertThat(newDevices.get(4).getId()).isEqualTo(TEST_DEVICE_5_ID);
+ }
+
+ @Test
+ public void getNotificationLargeIcon_withoutPackageName_returnsNull() {
+ mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
+ mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+ mNotificationEntryManager);
+
+ assertThat(mMediaOutputController.getNotificationIcon()).isNull();
+ }
+
+ @Test
+ public void getNotificationLargeIcon_withPackageNameAndMediaSession_returnsIconCompat() {
+ final List<NotificationEntry> entryList = new ArrayList<>();
+ final NotificationEntry entry = mock(NotificationEntry.class);
+ final StatusBarNotification sbn = mock(StatusBarNotification.class);
+ final Notification notification = mock(Notification.class);
+ final Icon icon = mock(Icon.class);
+ entryList.add(entry);
+
+ when(mNotificationEntryManager.getActiveNotificationsForCurrentUser())
+ .thenReturn(entryList);
+ when(entry.getSbn()).thenReturn(sbn);
+ when(sbn.getNotification()).thenReturn(notification);
+ when(sbn.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(notification.hasMediaSession()).thenReturn(true);
+ when(notification.getLargeIcon()).thenReturn(icon);
+
+ assertThat(mMediaOutputController.getNotificationIcon() instanceof IconCompat).isTrue();
+ }
+
+ @Test
+ public void getNotificationLargeIcon_withPackageNameAndNoMediaSession_returnsNull() {
+ final List<NotificationEntry> entryList = new ArrayList<>();
+ final NotificationEntry entry = mock(NotificationEntry.class);
+ final StatusBarNotification sbn = mock(StatusBarNotification.class);
+ final Notification notification = mock(Notification.class);
+ final Icon icon = mock(Icon.class);
+ entryList.add(entry);
+
+ when(mNotificationEntryManager.getActiveNotificationsForCurrentUser())
+ .thenReturn(entryList);
+ when(entry.getSbn()).thenReturn(sbn);
+ when(sbn.getNotification()).thenReturn(notification);
+ when(sbn.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(notification.hasMediaSession()).thenReturn(false);
+ when(notification.getLargeIcon()).thenReturn(icon);
+
+ assertThat(mMediaOutputController.getNotificationIcon()).isNull();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index 9ebb587..c1e7db1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -34,6 +34,7 @@
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
import org.junit.After;
@@ -58,6 +59,8 @@
private ActivityStarter mStarter = mock(ActivityStarter.class);
private LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class);
private MediaDevice mMediaDevice = mock(MediaDevice.class);
+ private NotificationEntryManager mNotificationEntryManager =
+ mock(NotificationEntryManager.class);
private MediaOutputDialog mMediaOutputDialog;
private MediaOutputController mMediaOutputController;
@@ -65,8 +68,9 @@
@Before
public void setUp() {
- mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
- mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
+ mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
+ mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+ mNotificationEntryManager);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputDialog = new MediaOutputDialog(mContext, false, mMediaOutputController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java
new file mode 100644
index 0000000..1f85112
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java
@@ -0,0 +1,248 @@
+/*
+ * 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.media.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.graphics.drawable.Icon;
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import androidx.core.graphics.drawable.IconCompat;
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class MediaOutputGroupAdapterTest extends SysuiTestCase {
+
+ private static final String TEST_DEVICE_NAME_1 = "test_device_name_1";
+ private static final String TEST_DEVICE_NAME_2 = "test_device_name_2";
+ private static final String TEST_DEVICE_ID_1 = "test_device_id_1";
+ private static final String TEST_DEVICE_ID_2 = "test_device_id_2";
+ private static final int TEST_VOLUME = 10;
+ private static final int TEST_MAX_VOLUME = 50;
+
+ // Mock
+ private MediaOutputController mMediaOutputController = mock(MediaOutputController.class);
+ private MediaDevice mMediaDevice1 = mock(MediaDevice.class);
+ private MediaDevice mMediaDevice2 = mock(MediaDevice.class);
+ private Icon mIcon = mock(Icon.class);
+ private IconCompat mIconCompat = mock(IconCompat.class);
+
+ private MediaOutputGroupAdapter mGroupAdapter;
+ private MediaOutputGroupAdapter.GroupViewHolder mGroupViewHolder;
+ private List<MediaDevice> mGroupMediaDevices = new ArrayList<>();
+ private List<MediaDevice> mSelectableMediaDevices = new ArrayList<>();
+ private List<MediaDevice> mSelectedMediaDevices = new ArrayList<>();
+ private List<MediaDevice> mDeselectableMediaDevices = new ArrayList<>();
+
+ @Before
+ public void setUp() {
+ when(mMediaOutputController.getGroupMediaDevices()).thenReturn(mGroupMediaDevices);
+ when(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).thenReturn(mIconCompat);
+ when(mMediaOutputController.getDeviceIconCompat(mMediaDevice2)).thenReturn(mIconCompat);
+ when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(mSelectableMediaDevices);
+ when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mSelectedMediaDevices);
+ when(mMediaOutputController.getDeselectableMediaDevice()).thenReturn(
+ mDeselectableMediaDevices);
+ when(mIconCompat.toIcon(mContext)).thenReturn(mIcon);
+ when(mMediaDevice1.getName()).thenReturn(TEST_DEVICE_NAME_1);
+ when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_ID_1);
+ when(mMediaDevice2.getName()).thenReturn(TEST_DEVICE_NAME_2);
+ when(mMediaDevice2.getId()).thenReturn(TEST_DEVICE_ID_2);
+ mGroupMediaDevices.add(mMediaDevice1);
+ mGroupMediaDevices.add(mMediaDevice2);
+ mSelectedMediaDevices.add(mMediaDevice1);
+ mSelectableMediaDevices.add(mMediaDevice2);
+ mDeselectableMediaDevices.add(mMediaDevice1);
+
+ mGroupAdapter = new MediaOutputGroupAdapter(mMediaOutputController);
+ mGroupViewHolder = (MediaOutputGroupAdapter.GroupViewHolder) mGroupAdapter
+ .onCreateViewHolder(new LinearLayout(mContext), 0);
+ }
+
+ @Test
+ public void onBindViewHolder_verifyGroupItem() {
+ mGroupAdapter.onBindViewHolder(mGroupViewHolder, 0);
+
+ assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(mContext.getText(
+ R.string.media_output_dialog_group));
+ }
+
+ @Test
+ public void onBindViewHolder_singleSelectedDevice_verifyView() {
+ mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);
+
+ assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
+ assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mCheckBox.isChecked()).isTrue();
+ // Disabled checkBox
+ assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onBindViewHolder_multipleSelectedDevice_verifyView() {
+ mSelectedMediaDevices.clear();
+ mSelectedMediaDevices.add(mMediaDevice1);
+ mSelectedMediaDevices.add(mMediaDevice2);
+ mDeselectableMediaDevices.clear();
+ mDeselectableMediaDevices.add(mMediaDevice1);
+ mDeselectableMediaDevices.add(mMediaDevice2);
+ mSelectableMediaDevices.clear();
+
+ mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);
+
+ assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
+ assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mCheckBox.isChecked()).isTrue();
+ // Enabled checkBox
+ assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void onBindViewHolder_notDeselectedDevice_verifyView() {
+ mSelectedMediaDevices.clear();
+ mSelectedMediaDevices.add(mMediaDevice1);
+ mSelectedMediaDevices.add(mMediaDevice2);
+ mDeselectableMediaDevices.clear();
+ mDeselectableMediaDevices.add(mMediaDevice1);
+ mSelectableMediaDevices.clear();
+
+ mGroupAdapter.onBindViewHolder(mGroupViewHolder, 2);
+
+ assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2);
+ assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mCheckBox.isChecked()).isTrue();
+ // Disabled checkBox
+ assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onBindViewHolder_selectableDevice_verifyCheckBox() {
+ mGroupAdapter.onBindViewHolder(mGroupViewHolder, 2);
+
+ assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2);
+ assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mGroupViewHolder.mCheckBox.isChecked()).isFalse();
+ // Enabled checkBox
+ assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void onBindViewHolder_verifySessionVolume() {
+ when(mMediaOutputController.getSessionVolume()).thenReturn(TEST_VOLUME);
+ when(mMediaOutputController.getSessionVolumeMax()).thenReturn(TEST_MAX_VOLUME);
+
+ mGroupAdapter.onBindViewHolder(mGroupViewHolder, 0);
+
+ assertThat(mGroupViewHolder.mSeekBar.getProgress()).isEqualTo(TEST_VOLUME);
+ assertThat(mGroupViewHolder.mSeekBar.getMax()).isEqualTo(TEST_MAX_VOLUME);
+ }
+
+ @Test
+ public void onBindViewHolder_verifyDeviceVolume() {
+ when(mMediaDevice1.getCurrentVolume()).thenReturn(TEST_VOLUME);
+ when(mMediaDevice1.getMaxVolume()).thenReturn(TEST_MAX_VOLUME);
+
+ mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);
+
+ assertThat(mGroupViewHolder.mSeekBar.getProgress()).isEqualTo(TEST_VOLUME);
+ assertThat(mGroupViewHolder.mSeekBar.getMax()).isEqualTo(TEST_MAX_VOLUME);
+ }
+
+ @Test
+ public void clickSelectedDevice_verifyRemoveDeviceFromPlayMedia() {
+ mSelectedMediaDevices.clear();
+ mSelectedMediaDevices.add(mMediaDevice1);
+ mSelectedMediaDevices.add(mMediaDevice2);
+ mDeselectableMediaDevices.clear();
+ mDeselectableMediaDevices.add(mMediaDevice1);
+ mDeselectableMediaDevices.add(mMediaDevice2);
+ mSelectableMediaDevices.clear();
+
+ mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);
+ mGroupViewHolder.mCheckBox.performClick();
+
+ verify(mMediaOutputController).removeDeviceFromPlayMedia(mMediaDevice1);
+ }
+
+ @Test
+ public void clickSelectabelDevice_verifyAddDeviceToPlayMedia() {
+ mGroupAdapter.onBindViewHolder(mGroupViewHolder, 2);
+
+ mGroupViewHolder.mCheckBox.performClick();
+
+ verify(mMediaOutputController).addDeviceToPlayMedia(mMediaDevice2);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
new file mode 100644
index 0000000..5813350
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.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 com.android.systemui.media.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.media.session.MediaSessionManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.media.LocalMediaManager;
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.phone.ShadeController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class MediaOutputGroupDialogTest extends SysuiTestCase {
+
+ private static final String TEST_PACKAGE = "test_package";
+
+ // Mock
+ private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
+ private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
+ private ShadeController mShadeController = mock(ShadeController.class);
+ private ActivityStarter mStarter = mock(ActivityStarter.class);
+ private LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class);
+ private MediaDevice mMediaDevice = mock(MediaDevice.class);
+ private MediaDevice mMediaDevice1 = mock(MediaDevice.class);
+ private NotificationEntryManager mNotificationEntryManager =
+ mock(NotificationEntryManager.class);
+
+ private MediaOutputGroupDialog mMediaOutputGroupDialog;
+ private MediaOutputController mMediaOutputController;
+ private List<MediaDevice> mMediaDevices = new ArrayList<>();
+
+ @Before
+ public void setUp() {
+ mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
+ mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
+ mNotificationEntryManager);
+ mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
+ mMediaOutputGroupDialog = new MediaOutputGroupDialog(mContext, false,
+ mMediaOutputController);
+ when(mLocalMediaManager.getSelectedMediaDevice()).thenReturn(mMediaDevices);
+ }
+
+ @After
+ public void tearDown() {
+ mMediaOutputGroupDialog.dismissDialog();
+ }
+
+ @Test
+ public void getStopButtonVisibility_returnVisible() {
+ assertThat(mMediaOutputGroupDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void getHeaderSubtitle_singleDevice_verifyTitle() {
+ mMediaDevices.add(mMediaDevice);
+
+ assertThat(mMediaOutputGroupDialog.getHeaderSubtitle()).isEqualTo(
+ mContext.getText(R.string.media_output_dialog_single_device));
+ }
+
+ @Test
+ public void getHeaderSubtitle_multipleDevices_verifyTitle() {
+ mMediaDevices.add(mMediaDevice);
+ mMediaDevices.add(mMediaDevice1);
+
+ assertThat(mMediaOutputGroupDialog.getHeaderSubtitle()).isEqualTo(mContext.getString(
+ R.string.media_output_dialog_multiple_devices, mMediaDevices.size()));
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
index cd94f84..c401fab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
@@ -23,6 +23,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.appops.AppOpsController
import com.android.systemui.dump.DumpManager
+import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.DeviceConfigProxy
import com.android.systemui.util.DeviceConfigProxyFake
@@ -65,6 +66,8 @@
private lateinit var dumpManager: DumpManager
@Mock
private lateinit var userTracker: UserTracker
+ @Mock
+ private lateinit var logger: PrivacyLogger
private lateinit var privacyItemController: PrivacyItemController
private lateinit var executor: FakeExecutor
@@ -77,8 +80,8 @@
executor,
deviceConfigProxy,
userTracker,
- dumpManager
- )
+ logger,
+ dumpManager)
}
@Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index 16a1105..3e83498 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -29,10 +29,12 @@
import com.android.systemui.appops.AppOpItem
import com.android.systemui.appops.AppOpsController
import com.android.systemui.dump.DumpManager
+import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.DeviceConfigProxy
import com.android.systemui.util.DeviceConfigProxyFake
import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.time.FakeSystemClock
import org.hamcrest.Matchers.hasItem
import org.hamcrest.Matchers.not
@@ -86,6 +88,8 @@
private lateinit var userTracker: UserTracker
@Mock
private lateinit var dumpManager: DumpManager
+ @Mock
+ private lateinit var logger: PrivacyLogger
@Captor
private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>>
@Captor
@@ -102,8 +106,8 @@
executor,
deviceConfigProxy,
userTracker,
- dumpManager
- )
+ logger,
+ dumpManager)
}
@Before
@@ -300,6 +304,45 @@
verify(callback, never()).onPrivacyItemsChanged(any())
}
+ @Test
+ fun testLogActiveChanged() {
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+
+ verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+ argCaptorCallback.value.onActiveStateChanged(
+ AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true)
+
+ verify(logger).logUpdatedItemFromAppOps(
+ AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true)
+ }
+
+ @Test
+ fun testLogListUpdated() {
+ doReturn(listOf(
+ AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0))
+ ).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+
+ verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+ argCaptorCallback.value.onActiveStateChanged(
+ AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true)
+ executor.runAllReady()
+
+ val expected = PrivacyItem(
+ PrivacyType.TYPE_LOCATION,
+ PrivacyApplication(TEST_PACKAGE_NAME, TEST_UID)
+ )
+
+ val captor = argumentCaptor<String>()
+ verify(logger, atLeastOnce()).logUpdatedPrivacyItemsList(capture(captor))
+ // Let's look at the last log
+ val values = captor.allValues
+ assertTrue(values[values.size - 1].contains(expected.toLog()))
+ }
+
private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
new file mode 100644
index 0000000..26b5d26
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -0,0 +1,234 @@
+/*
+ * 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.qs
+
+import android.content.Context
+import android.testing.AndroidTestingRunner
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.demomode.DemoModeController
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.privacy.PrivacyItemController
+import com.android.systemui.privacy.logging.PrivacyLogger
+import com.android.systemui.qs.carrier.QSCarrierGroup
+import com.android.systemui.qs.carrier.QSCarrierGroupController
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.phone.StatusBarIconController
+import com.android.systemui.statusbar.phone.StatusIconContainer
+import com.android.systemui.statusbar.policy.Clock
+import com.android.systemui.statusbar.policy.NextAlarmController
+import com.android.systemui.util.RingerModeTracker
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.utils.leaks.FakeZenModeController
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Answers
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var view: QuickStatusBarHeader
+ @Mock
+ private lateinit var zenModeController: FakeZenModeController
+ @Mock
+ private lateinit var nextAlarmController: NextAlarmController
+ @Mock
+ private lateinit var privacyItemController: PrivacyItemController
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private lateinit var ringerModeTracker: RingerModeTracker
+ @Mock
+ private lateinit var activityStarter: ActivityStarter
+ @Mock
+ private lateinit var uiEventLogger: UiEventLogger
+ @Mock
+ private lateinit var qsTileHost: QSTileHost
+ @Mock
+ private lateinit var statusBarIconController: StatusBarIconController
+ @Mock
+ private lateinit var commandQueue: CommandQueue
+ @Mock
+ private lateinit var demoModeController: DemoModeController
+ @Mock
+ private lateinit var userTracker: UserTracker
+ @Mock
+ private lateinit var quickQSPanelController: QuickQSPanelController
+ @Mock(answer = Answers.RETURNS_SELF)
+ private lateinit var qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder
+ @Mock
+ private lateinit var qsCarrierGroupController: QSCarrierGroupController
+ @Mock
+ private lateinit var privacyLogger: PrivacyLogger
+ @Mock
+ private lateinit var iconContainer: StatusIconContainer
+ @Mock
+ private lateinit var qsCarrierGroup: QSCarrierGroup
+ @Mock
+ private lateinit var privacyChip: OngoingPrivacyChip
+ @Mock
+ private lateinit var clock: Clock
+ @Mock
+ private lateinit var mockView: View
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private lateinit var context: Context
+
+ private lateinit var controller: QuickStatusBarHeaderController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ stubViews()
+ `when`(iconContainer.context).thenReturn(context)
+ `when`(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController)
+ `when`(view.resources).thenReturn(mContext.resources)
+ `when`(view.isAttachedToWindow).thenReturn(true)
+
+ controller = QuickStatusBarHeaderController(
+ view,
+ zenModeController,
+ nextAlarmController,
+ privacyItemController,
+ ringerModeTracker,
+ activityStarter,
+ uiEventLogger,
+ qsTileHost,
+ statusBarIconController,
+ commandQueue,
+ demoModeController,
+ userTracker,
+ quickQSPanelController,
+ qsCarrierGroupControllerBuilder,
+ privacyLogger
+ )
+ }
+
+ @After
+ fun tearDown() {
+ controller.onViewDetached()
+ }
+
+ @Test
+ fun testIgnoredSlotsOnAttached_noIndicators() {
+ setPrivacyController(false, false, false)
+
+ controller.init()
+
+ val captor = argumentCaptor<List<String>>()
+ verify(iconContainer).setIgnoredSlots(capture(captor))
+
+ assertThat(captor.value).isEmpty()
+ }
+
+ @Test
+ fun testIgnoredSlotsOnAttached_onlyMicCamera() {
+ setPrivacyController(false, true, false)
+
+ controller.init()
+
+ val captor = argumentCaptor<List<String>>()
+ verify(iconContainer).setIgnoredSlots(capture(captor))
+
+ val cameraString = mContext.resources.getString(
+ com.android.internal.R.string.status_bar_camera)
+ val micString = mContext.resources.getString(
+ com.android.internal.R.string.status_bar_microphone)
+
+ assertThat(captor.value).containsExactly(cameraString, micString)
+ }
+
+ @Test
+ fun testIgnoredSlotsOnAttached_onlyLocation() {
+ setPrivacyController(false, false, true)
+
+ controller.init()
+
+ val captor = argumentCaptor<List<String>>()
+ verify(iconContainer).setIgnoredSlots(capture(captor))
+
+ val locationString = mContext.resources.getString(
+ com.android.internal.R.string.status_bar_location)
+
+ assertThat(captor.value).containsExactly(locationString)
+ }
+
+ @Test
+ fun testIgnoredSlotsOnAttached_locationMicCamera() {
+ setPrivacyController(false, true, true)
+
+ controller.init()
+
+ val captor = argumentCaptor<List<String>>()
+ verify(iconContainer).setIgnoredSlots(capture(captor))
+
+ val cameraString = mContext.resources.getString(
+ com.android.internal.R.string.status_bar_camera)
+ val micString = mContext.resources.getString(
+ com.android.internal.R.string.status_bar_microphone)
+ val locationString = mContext.resources.getString(
+ com.android.internal.R.string.status_bar_location)
+
+ assertThat(captor.value).containsExactly(cameraString, micString, locationString)
+ }
+
+ @Test
+ fun testIgnoredSlotsOnAttached_all() {
+ setPrivacyController(true, false, false)
+
+ controller.init()
+
+ val captor = argumentCaptor<List<String>>()
+ verify(iconContainer).setIgnoredSlots(capture(captor))
+
+ val cameraString = mContext.resources.getString(
+ com.android.internal.R.string.status_bar_camera)
+ val micString = mContext.resources.getString(
+ com.android.internal.R.string.status_bar_microphone)
+ val locationString = mContext.resources.getString(
+ com.android.internal.R.string.status_bar_location)
+
+ assertThat(captor.value).containsExactly(cameraString, micString, locationString)
+ }
+
+ private fun stubViews() {
+ `when`(view.findViewById<View>(anyInt())).thenReturn(mockView)
+ `when`(view.findViewById<QSCarrierGroup>(R.id.carrier_group)).thenReturn(qsCarrierGroup)
+ `when`(view.findViewById<StatusIconContainer>(R.id.statusIcons)).thenReturn(iconContainer)
+ `when`(view.findViewById<OngoingPrivacyChip>(R.id.privacy_chip)).thenReturn(privacyChip)
+ `when`(view.findViewById<Clock>(R.id.clock)).thenReturn(clock)
+ }
+
+ private fun setPrivacyController(all: Boolean, micCamera: Boolean, location: Boolean) {
+ `when`(privacyItemController.allIndicatorsAvailable).thenReturn(all)
+ `when`(privacyItemController.micCameraAvailable).thenReturn(micCamera)
+ `when`(privacyItemController.locationAvailable).thenReturn(location)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 808f17b..2a50273 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -18,7 +18,6 @@
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.content.Intent.ACTION_USER_SWITCHED;
-import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_MEDIA_CONTROLS;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_PEOPLE;
@@ -322,8 +321,6 @@
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
Settings.Secure.putInt(mContext.getContentResolver(),
- NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
- Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1);
NotificationEntry entry = new NotificationEntryBuilder()
@@ -339,8 +336,6 @@
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
Settings.Secure.putInt(mContext.getContentResolver(),
- NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
- Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
final Notification notification = mock(Notification.class);
@@ -358,8 +353,6 @@
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
Settings.Secure.putInt(mContext.getContentResolver(),
- NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
- Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
final Notification notification = mock(Notification.class);
@@ -377,8 +370,6 @@
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
Settings.Secure.putInt(mContext.getContentResolver(),
- NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
- Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
final Notification notification = mock(Notification.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index 9971e0c..71f146b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -74,8 +74,6 @@
mMockResources = mock(Resources.class);
mPackageManagerSpy = spy(getContext().getPackageManager());
doReturn(mMockResources).when(mPackageManagerSpy)
- .getResourcesForApplicationAsUser(eq("mockPackage"), anyInt());
- doReturn(mMockResources).when(mPackageManagerSpy)
.getResourcesForApplication(eq("mockPackage"));
doReturn(mMockResources).when(mPackageManagerSpy).getResourcesForApplication(argThat(
(ArgumentMatcher<ApplicationInfo>) o -> "mockPackage".equals(o.packageName)));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
index 8948fd0..3a948f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
@@ -18,7 +18,6 @@
import android.provider.DeviceConfig
import android.provider.Settings
-import android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
@@ -43,8 +42,6 @@
@Before
public fun setup() {
- Settings.Secure.putInt(mContext.getContentResolver(),
- NOTIFICATION_NEW_INTERRUPTION_MODEL, 1)
manager = NotificationSectionsFeatureManager(proxyFake, mContext)
manager!!.clearCache()
originalQsMediaPlayer = Settings.System.getInt(context.getContentResolver(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 9465a3d..61edcf9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -995,7 +995,7 @@
mTestHandler, null, Optional.of(mBubbles));
verify(mMockINotificationManager, times(1)).createConversationNotificationChannelForPackage(
- anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID));
+ anyString(), anyInt(), any(), eq(CONVERSATION_ID));
}
@Test
@@ -1020,7 +1020,7 @@
mTestHandler, null, Optional.of(mBubbles));
verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage(
- anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID));
+ anyString(), anyInt(), any(), eq(CONVERSATION_ID));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 02a3e11..b482968 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -22,7 +22,6 @@
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME;
-import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
@@ -50,7 +49,6 @@
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
-import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -69,7 +67,6 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -163,15 +160,6 @@
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
new Notification(), UserHandle.CURRENT, null, 0);
mEntry = new NotificationEntryBuilder().setSbn(mSbn).build();
-
- Settings.Secure.putInt(mContext.getContentResolver(),
- NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
- }
-
- @After
- public void tearDown() {
- Settings.Secure.putInt(mContext.getContentResolver(),
- NOTIFICATION_NEW_INTERRUPTION_MODEL, 0);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index 99e8c7e..37e8218 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -14,7 +14,6 @@
package com.android.systemui.statusbar.notification.row;
-import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL;
import static android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE;
import static junit.framework.Assert.assertEquals;
@@ -43,7 +42,6 @@
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.utils.leaks.LeakCheckedTest;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -66,12 +64,6 @@
when(mRow.getEntry()).thenReturn(entry);
}
- @After
- public void tearDown() {
- Settings.Secure.putInt(mContext.getContentResolver(),
- NOTIFICATION_NEW_INTERRUPTION_MODEL, 0);
- }
-
@Test
public void testAttachDetach() {
NotificationMenuRowPlugin row =
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 bb85cec..ceb4d84 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
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.notification.stack;
import static android.provider.Settings.Secure.NOTIFICATION_HISTORY_ENABLED;
-import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
@@ -64,7 +63,6 @@
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
-import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
@@ -102,17 +100,12 @@
@Mock private SysuiStatusBarStateController mStatusBarStateController;
@Mock private NotificationSwipeHelper mNotificationSwipeHelper;
@Mock private NotificationStackScrollLayoutController mStackScrollLayoutController;
- private int mOriginalInterruptionModelSetting;
@Before
@UiThreadTest
public void setUp() throws Exception {
allowTestableLooperAsMainThread();
- mOriginalInterruptionModelSetting = Settings.Secure.getInt(mContext.getContentResolver(),
- NOTIFICATION_NEW_INTERRUPTION_MODEL, 0);
- Settings.Secure.putInt(mContext.getContentResolver(),
- NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
Settings.Secure.putIntForUser(mContext.getContentResolver(), NOTIFICATION_HISTORY_ENABLED,
1, UserHandle.USER_CURRENT);
@@ -166,12 +159,6 @@
doNothing().when(notificationShelf).setAnimationsEnabled(anyBoolean());
}
- @After
- public void tearDown() {
- Settings.Secure.putInt(mContext.getContentResolver(),
- NOTIFICATION_NEW_INTERRUPTION_MODEL, mOriginalInterruptionModelSetting);
- }
-
@Test
public void testNotDimmedOnKeyguard() {
when(mBarState.getState()).thenReturn(StatusBarState.SHADE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
index ede5fce..a1d419c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
@@ -25,6 +25,7 @@
import androidx.test.filters.SmallTest;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.demomode.DemoModeController;
@@ -76,7 +77,6 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
-
mController = new NotificationIconAreaController(
mContext,
mStatusBarStateController,
@@ -89,7 +89,6 @@
mDemoModeController,
mDarkIconDispatcher,
mStatusBarWindowController);
- mController.setupAodIcons(mAodIcons);
}
@Test
@@ -108,6 +107,9 @@
@Test
public void testAppearResetsTranslation() {
+ mController.setupAodIcons(
+ mAodIcons,
+ KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL);
when(mDozeParameters.shouldControlScreenOff()).thenReturn(false);
mController.appearAodIcons();
verify(mAodIcons).setTranslationY(0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index dd7f263..d0bf281 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -27,7 +27,6 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.model.SysUiState;
@@ -38,8 +37,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tracing.ProtoTracer;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.ShellDump;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedGestureHandler;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
@@ -54,7 +52,6 @@
import org.mockito.MockitoAnnotations;
import java.util.Optional;
-import java.util.concurrent.ExecutionException;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -74,9 +71,8 @@
@Mock PipTouchHandler mPipTouchHandler;
@Mock SplitScreen mSplitScreen;
@Mock OneHanded mOneHanded;
- @Mock ShellTaskOrganizer mTaskOrganizer;
@Mock ProtoTracer mProtoTracer;
- @Mock PackageManager mMockPackageManager;
+ @Mock ShellDump mShellDump;
@Before
public void setUp() {
@@ -86,7 +82,8 @@
mWMShell = new WMShell(mContext, mCommandQueue, mConfigurationController,
mInputConsumerController, mKeyguardUpdateMonitor, mTaskStackChangeListeners,
mNavigationModeController, mScreenLifecycle, mSysUiState, Optional.of(mPip),
- Optional.of(mSplitScreen), Optional.of(mOneHanded), mTaskOrganizer, mProtoTracer);
+ Optional.of(mSplitScreen), Optional.of(mOneHanded), mProtoTracer,
+ Optional.of(mShellDump));
when(mPip.getPipTouchHandler()).thenReturn(mPipTouchHandler);
}
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index 613d28b..5526c65 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -37,7 +37,6 @@
libs: [
"framework-statsd.stubs.module_lib",
"framework-tethering.impl",
- "framework-telephony-stubs",
"framework-wifi",
"unsupportedappusage",
],
diff --git a/packages/Tethering/apex/manifest.json b/packages/Tethering/apex/manifest.json
index 538ffb3..11e205d 100644
--- a/packages/Tethering/apex/manifest.json
+++ b/packages/Tethering/apex/manifest.json
@@ -1,4 +1,4 @@
{
"name": "com.android.tethering",
- "version": 300000000
+ "version": 309999900
}
diff --git a/packages/Tethering/tests/Android.bp b/packages/Tethering/tests/Android.bp
new file mode 100644
index 0000000..cb0a20b
--- /dev/null
+++ b/packages/Tethering/tests/Android.bp
@@ -0,0 +1,23 @@
+//
+// 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.
+//
+
+filegroup {
+ name: "TetheringTestsJarJarRules",
+ srcs: ["jarjar-rules.txt"],
+ visibility: [
+ "//frameworks/base/packages/Tethering/tests:__subpackages__",
+ ]
+}
diff --git a/packages/Tethering/tests/integration/Android.bp b/packages/Tethering/tests/integration/Android.bp
index 02bab9b..5765c01 100644
--- a/packages/Tethering/tests/integration/Android.bp
+++ b/packages/Tethering/tests/integration/Android.bp
@@ -79,6 +79,7 @@
// For NetworkStackUtils included in NetworkStackBase
"libnetworkstackutilsjni",
],
+ jarjar_rules: ":TetheringTestsJarJarRules",
compile_multilib: "both",
manifest: "AndroidManifest_coverage.xml",
-}
\ No newline at end of file
+}
diff --git a/packages/Tethering/tests/unit/jarjar-rules.txt b/packages/Tethering/tests/jarjar-rules.txt
similarity index 90%
rename from packages/Tethering/tests/unit/jarjar-rules.txt
rename to packages/Tethering/tests/jarjar-rules.txt
index 7ed8963..c99ff7f 100644
--- a/packages/Tethering/tests/unit/jarjar-rules.txt
+++ b/packages/Tethering/tests/jarjar-rules.txt
@@ -10,7 +10,10 @@
rule android.util.LocalLog* com.android.networkstack.tethering.util.LocalLog@1
+# Classes from net-utils-framework-common
+rule com.android.net.module.util.** com.android.networkstack.tethering.util.@1
+
# TODO: either stop using frameworks-base-testutils or remove the unit test classes it contains.
# TestableLooper from "testables" can be used instead of TestLooper from frameworks-base-testutils.
zap android.os.test.TestLooperTest*
-zap com.android.test.filters.SelectTestTests*
\ No newline at end of file
+zap com.android.test.filters.SelectTestTests*
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
index 0413714..3589725 100644
--- a/packages/Tethering/tests/unit/Android.bp
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -59,7 +59,6 @@
"ext",
"framework-minus-apex",
"framework-res",
- "framework-telephony-stubs",
"framework-tethering.impl",
"framework-wifi.stubs.module_lib",
],
@@ -68,7 +67,6 @@
"libdexmakerjvmtiagent",
"libstaticjvmtiagent",
],
- jarjar_rules: "jarjar-rules.txt",
}
// Library containing the unit tests. This is used by the coverage test target to pull in the
@@ -89,6 +87,7 @@
"device-tests",
"mts",
],
+ jarjar_rules: ":TetheringTestsJarJarRules",
defaults: ["TetheringTestsDefaults"],
compile_multilib: "both",
}
diff --git a/packages/WAPPushManager/Android.bp b/packages/WAPPushManager/Android.bp
index 083dac9..0b62c72 100644
--- a/packages/WAPPushManager/Android.bp
+++ b/packages/WAPPushManager/Android.bp
@@ -2,6 +2,7 @@
android_app {
name: "WAPPushManager",
+ defaults: ["platform_app_defaults"],
srcs: ["src/**/*.java"],
platform_apis: true,
libs: ["telephony-common"],
diff --git a/packages/WallpaperBackup/Android.bp b/packages/WallpaperBackup/Android.bp
index 748eb40..e52d53e 100644
--- a/packages/WallpaperBackup/Android.bp
+++ b/packages/WallpaperBackup/Android.bp
@@ -16,6 +16,7 @@
android_app {
name: "WallpaperBackup",
+ defaults: ["platform_app_defaults"],
srcs: ["src/**/*.java"],
optimize: {
proguard_flags_files: ["proguard.flags"],
diff --git a/packages/WallpaperCropper/Android.bp b/packages/WallpaperCropper/Android.bp
index ac38b27..df97a3c0 100644
--- a/packages/WallpaperCropper/Android.bp
+++ b/packages/WallpaperCropper/Android.bp
@@ -1,5 +1,6 @@
android_app {
name: "WallpaperCropper",
+ defaults: ["platform_app_defaults"],
srcs: ["src/**/*.java"],
platform_apis: true,
certificate: "platform",
diff --git a/services/Android.bp b/services/Android.bp
index 8c9c487..eb7b72e 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -1,17 +1,3 @@
-java_defaults {
- name: "services_defaults",
- plugins: [
- "error_prone_android_framework",
- ],
- errorprone: {
- javacflags: [
- "-Xep:AndroidFrameworkBinderIdentity:ERROR",
- "-Xep:AndroidFrameworkCompatChange:ERROR",
- "-Xep:AndroidFrameworkUid:ERROR",
- ],
- },
-}
-
filegroup {
name: "services-main-sources",
srcs: ["java/**/*.java"],
diff --git a/services/accessibility/Android.bp b/services/accessibility/Android.bp
index 21a0c74..65313fc 100644
--- a/services/accessibility/Android.bp
+++ b/services/accessibility/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.accessibility",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [":services.accessibility-sources"],
libs: ["services.core"],
}
diff --git a/services/appprediction/Android.bp b/services/appprediction/Android.bp
index c12f62f..bc43db1 100644
--- a/services/appprediction/Android.bp
+++ b/services/appprediction/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.appprediction",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [":services.appprediction-sources"],
libs: ["services.core"],
}
diff --git a/services/appwidget/Android.bp b/services/appwidget/Android.bp
index 83a9aa4..e46e5c8 100644
--- a/services/appwidget/Android.bp
+++ b/services/appwidget/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.appwidget",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [":services.appwidget-sources"],
libs: ["services.core"],
}
diff --git a/services/autofill/Android.bp b/services/autofill/Android.bp
index 1e65e84..c448066 100644
--- a/services/autofill/Android.bp
+++ b/services/autofill/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.autofill",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [":services.autofill-sources"],
libs: ["services.core"],
}
diff --git a/services/backup/Android.bp b/services/backup/Android.bp
index 56b788e..b5444f4 100644
--- a/services/backup/Android.bp
+++ b/services/backup/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.backup",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [":services.backup-sources"],
libs: ["services.core"],
static_libs: ["backuplib"],
diff --git a/services/companion/Android.bp b/services/companion/Android.bp
index e251042..6aa54c4 100644
--- a/services/companion/Android.bp
+++ b/services/companion/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.companion",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [":services.companion-sources"],
libs: ["services.core"],
}
diff --git a/services/contentcapture/Android.bp b/services/contentcapture/Android.bp
index 7006430..688c0b1 100644
--- a/services/contentcapture/Android.bp
+++ b/services/contentcapture/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.contentcapture",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [":services.contentcapture-sources"],
libs: ["services.core"],
}
diff --git a/services/contentsuggestions/Android.bp b/services/contentsuggestions/Android.bp
index 3fe3cd2..1b4d7e2 100644
--- a/services/contentsuggestions/Android.bp
+++ b/services/contentsuggestions/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.contentsuggestions",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [":services.contentsuggestions-sources"],
libs: ["services.core"],
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 1a7f0d1..ff47a83b 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -73,6 +73,7 @@
":vold_aidl",
":platform-compat-config",
":display-device-config",
+ ":cec-config",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",
"java/com/android/server/wm/EventLogTags.logtags",
@@ -116,6 +117,7 @@
"android.hardware.contexthub-V1.0-java",
"android.hardware.rebootescrow-java",
"android.hardware.soundtrigger-V2.3-java",
+ "android.hardware.power.stats-java",
"android.hidl.manager-V1.2-java",
"capture_state_listener-aidl-java",
"dnsresolver_aidl_interface-java",
@@ -142,7 +144,7 @@
java_library {
name: "services.core",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
static_libs: ["services.core.priorityboosted"],
}
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 43c54b4..70cf045 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -60,6 +60,27 @@
* @hide Only for use within the system server.
*/
public abstract class PackageManagerInternal {
+ @IntDef(prefix = "PACKAGE_", value = {
+ PACKAGE_SYSTEM,
+ PACKAGE_SETUP_WIZARD,
+ PACKAGE_INSTALLER,
+ PACKAGE_VERIFIER,
+ PACKAGE_BROWSER,
+ PACKAGE_SYSTEM_TEXT_CLASSIFIER,
+ PACKAGE_PERMISSION_CONTROLLER,
+ PACKAGE_WELLBEING,
+ PACKAGE_DOCUMENTER,
+ PACKAGE_CONFIGURATOR,
+ PACKAGE_INCIDENT_REPORT_APPROVER,
+ PACKAGE_APP_PREDICTOR,
+ PACKAGE_OVERLAY_CONFIG_SIGNATURE,
+ PACKAGE_WIFI,
+ PACKAGE_COMPANION,
+ PACKAGE_RETAIL_DEMO,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface KnownPackage {}
+
public static final int PACKAGE_SYSTEM = 0;
public static final int PACKAGE_SETUP_WIZARD = 1;
public static final int PACKAGE_INSTALLER = 2;
@@ -72,11 +93,13 @@
public static final int PACKAGE_CONFIGURATOR = 9;
public static final int PACKAGE_INCIDENT_REPORT_APPROVER = 10;
public static final int PACKAGE_APP_PREDICTOR = 11;
+ public static final int PACKAGE_OVERLAY_CONFIG_SIGNATURE = 12;
public static final int PACKAGE_WIFI = 13;
public static final int PACKAGE_COMPANION = 14;
public static final int PACKAGE_RETAIL_DEMO = 15;
- public static final int PACKAGE_OVERLAY_CONFIG_SIGNATURE = 16;
- public static final int LAST_KNOWN_PACKAGE = PACKAGE_OVERLAY_CONFIG_SIGNATURE;
+ // Integer value of the last known package ID. Increases as new ID is added to KnownPackage.
+ // Please note the numbers should be continuous.
+ public static final int LAST_KNOWN_PACKAGE = PACKAGE_RETAIL_DEMO;
@IntDef(flag = true, prefix = "RESOLVE_", value = {
RESOLVE_NON_BROWSER_ONLY,
@@ -118,26 +141,6 @@
*/
public static final int INTEGRITY_VERIFICATION_REJECT = 0;
- @IntDef(value = {
- PACKAGE_SYSTEM,
- PACKAGE_SETUP_WIZARD,
- PACKAGE_INSTALLER,
- PACKAGE_VERIFIER,
- PACKAGE_BROWSER,
- PACKAGE_SYSTEM_TEXT_CLASSIFIER,
- PACKAGE_PERMISSION_CONTROLLER,
- PACKAGE_WELLBEING,
- PACKAGE_DOCUMENTER,
- PACKAGE_CONFIGURATOR,
- PACKAGE_INCIDENT_REPORT_APPROVER,
- PACKAGE_APP_PREDICTOR,
- PACKAGE_WIFI,
- PACKAGE_COMPANION,
- PACKAGE_RETAIL_DEMO,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface KnownPackage {}
-
/** Observer called whenever the list of packages changes */
public interface PackageListObserver {
/** A package was added to the system. */
@@ -1106,4 +1109,21 @@
}
}
+ /**
+ * Retrieve all of the information we know about a particular activity class including its
+ * package states.
+ *
+ * @param packageName a specific package
+ * @param filterCallingUid The results will be filtered in the context of this UID instead
+ * of the calling UID.
+ * @param userId The user for whom the package is installed
+ * @return IncrementalStatesInfo that contains information about package states.
+ */
+ public abstract IncrementalStatesInfo getIncrementalStatesInfo(String packageName,
+ int filterCallingUid, int userId);
+
+ /**
+ * Notifies that a package has crashed or ANR'd.
+ */
+ public abstract void notifyPackageCrashOrAnr(String packageName);
}
diff --git a/services/core/java/android/content/pm/TestUtilityService.java b/services/core/java/android/content/pm/TestUtilityService.java
new file mode 100644
index 0000000..426352b
--- /dev/null
+++ b/services/core/java/android/content/pm/TestUtilityService.java
@@ -0,0 +1,30 @@
+/*
+ * 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.os.IBinder;
+
+/**
+ * Utility methods for testing and debugging.
+ */
+public interface TestUtilityService {
+ /**
+ * Verifies validity of the token passed as a parameter to holdLock(). Throws an exception if
+ * the token is invalid.
+ */
+ void verifyHoldLockToken(IBinder token);
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0cd6e0810..85d77f2 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -929,13 +929,6 @@
}
/**
- * @see ServiceManager#checkService(String)
- */
- public boolean hasService(@NonNull String name) {
- return ServiceManager.checkService(name) != null;
- }
-
- /**
* @see IpConnectivityMetrics.Logger
*/
public IpConnectivityMetrics.Logger getMetricsLogger() {
@@ -1078,7 +1071,8 @@
// Do the same for Ethernet, since it's often not specified in the configs, although many
// devices can use it via USB host adapters.
- if (mNetConfigs[TYPE_ETHERNET] == null && mDeps.hasService(Context.ETHERNET_SERVICE)) {
+ if (mNetConfigs[TYPE_ETHERNET] == null
+ && mContext.getSystemService(Context.ETHERNET_SERVICE) != null) {
mLegacyTypeTracker.addSupportedType(TYPE_ETHERNET);
mNetworksDefined++;
}
@@ -4004,13 +3998,11 @@
settingsPkgName + ".wifi.WifiNoInternetDialog");
}
- PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
- mContext,
+ PendingIntent pendingIntent = PendingIntent.getActivity(
+ mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
0 /* requestCode */,
intent,
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
- null /* options */,
- UserHandle.CURRENT);
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
mNotifier.showNotification(nai.network.netId, type, nai, null, pendingIntent, highPriority);
}
diff --git a/services/core/java/com/android/server/Dumpable.java b/services/core/java/com/android/server/Dumpable.java
new file mode 100644
index 0000000..d2bd66f
--- /dev/null
+++ b/services/core/java/com/android/server/Dumpable.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.IndentingPrintWriter;
+
+/**
+ * Interface used to dump {@link SystemServer} state that is not associated with any service.
+ */
+public interface Dumpable {
+
+ /**
+ * Dumps the state.
+ */
+ void dump(@NonNull IndentingPrintWriter pw, @Nullable String[] args);
+
+ /**
+ * Gets the name of the dumpable.
+ *
+ * <p>If not overridden, will return the simple class name.
+ */
+ default String getDumpableName() {
+ return Dumpable.this.getClass().getSimpleName();
+ }
+}
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 20f68da..d4e912b6 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -75,9 +75,9 @@
@VisibleForTesting static final long POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS = 500;
/**
- * Number of taps required to launch panic ui.
+ * Number of taps required to launch emergency gesture ui.
*/
- private static final int PANIC_POWER_TAP_COUNT_THRESHOLD = 5;
+ private static final int EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD = 5;
/**
* Number of taps required to launch camera shortcut.
@@ -138,9 +138,9 @@
private boolean mCameraDoubleTapPowerEnabled;
/**
- * Whether panic button gesture is currently enabled
+ * Whether emergency gesture is currently enabled
*/
- private boolean mPanicButtonGestureEnabled;
+ private boolean mEmergencyGestureEnabled;
private long mLastPowerDown;
private int mPowerButtonConsecutiveTaps;
@@ -178,7 +178,7 @@
"GestureLauncherService");
updateCameraRegistered();
updateCameraDoubleTapPowerEnabled();
- updatePanicButtonGestureEnabled();
+ updateEmergencyGestureEnabled();
mUserId = ActivityManager.getCurrentUser();
mContext.registerReceiver(mUserReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED));
@@ -197,7 +197,7 @@
Settings.Secure.getUriFor(Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED),
false, mSettingObserver, mUserId);
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.PANIC_GESTURE_ENABLED),
+ Settings.Secure.getUriFor(Settings.Secure.EMERGENCY_GESTURE_ENABLED),
false, mSettingObserver, mUserId);
}
@@ -225,10 +225,10 @@
}
@VisibleForTesting
- void updatePanicButtonGestureEnabled() {
- boolean enabled = isPanicButtonGestureEnabled(mContext, mUserId);
+ void updateEmergencyGestureEnabled() {
+ boolean enabled = isEmergencyGestureEnabled(mContext, mUserId);
synchronized (this) {
- mPanicButtonGestureEnabled = enabled;
+ mEmergencyGestureEnabled = enabled;
}
}
@@ -357,11 +357,11 @@
}
/**
- * Whether to enable panic button gesture.
+ * Whether to enable emergency gesture.
*/
- public static boolean isPanicButtonGestureEnabled(Context context, int userId) {
+ public static boolean isEmergencyGestureEnabled(Context context, int userId) {
return Settings.Secure.getIntForUser(context.getContentResolver(),
- Settings.Secure.PANIC_GESTURE_ENABLED, 0, userId) != 0;
+ Settings.Secure.EMERGENCY_GESTURE_ENABLED, 0, userId) != 0;
}
/**
@@ -409,7 +409,7 @@
return false;
}
boolean launchCamera = false;
- boolean launchPanic = false;
+ boolean launchEmergencyGesture = false;
boolean intercept = false;
long powerTapInterval;
synchronized (this) {
@@ -428,15 +428,15 @@
mPowerButtonConsecutiveTaps++;
mPowerButtonSlowConsecutiveTaps++;
}
- // Check if we need to launch camera or panic flows
- if (mPanicButtonGestureEnabled) {
+ // Check if we need to launch camera or emergency gesture flows
+ if (mEmergencyGestureEnabled) {
// Commit to intercepting the powerkey event after the second "quick" tap to avoid
- // lockscreen changes between launching camera and the panic flow.
+ // lockscreen changes between launching camera and the emergency gesture flow.
if (mPowerButtonConsecutiveTaps > 1) {
intercept = interactive;
}
- if (mPowerButtonConsecutiveTaps == PANIC_POWER_TAP_COUNT_THRESHOLD) {
- launchPanic = true;
+ if (mPowerButtonConsecutiveTaps == EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD) {
+ launchEmergencyGesture = true;
}
}
if (mCameraDoubleTapPowerEnabled
@@ -461,18 +461,18 @@
mMetricsLogger.action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE,
(int) powerTapInterval);
}
- } else if (launchPanic) {
- Slog.i(TAG, "Panic gesture detected, launching panic.");
- launchPanic = handlePanicButtonGesture();
+ } else if (launchEmergencyGesture) {
+ Slog.i(TAG, "Emergency gesture detected, launching.");
+ launchEmergencyGesture = handleEmergencyGesture();
// TODO(b/160006048): Add logging
}
mMetricsLogger.histogram("power_consecutive_short_tap_count",
mPowerButtonSlowConsecutiveTaps);
mMetricsLogger.histogram("power_double_tap_interval", (int) powerTapInterval);
- outLaunched.value = launchCamera || launchPanic;
- // Intercept power key event if the press is part of a gesture (camera, panic) and the user
- // has completed setup.
+ outLaunched.value = launchCamera || launchEmergencyGesture;
+ // Intercept power key event if the press is part of a gesture (camera, eGesture) and the
+ // user has completed setup.
return intercept && isUserSetupComplete();
}
@@ -512,27 +512,25 @@
}
/**
- * @return true if panic ui was launched, false otherwise.
+ * @return true if emergency gesture UI was launched, false otherwise.
*/
@VisibleForTesting
- boolean handlePanicButtonGesture() {
- // TODO(b/160006048): This is the wrong way to launch panic ui. Rewrite this to go
- // through SysUI
+ boolean handleEmergencyGesture() {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- "GestureLauncher:handlePanicButtonGesture");
+ "GestureLauncher:handleEmergencyGesture");
try {
boolean userSetupComplete = isUserSetupComplete();
if (!userSetupComplete) {
if (DBG) {
Slog.d(TAG, String.format(
- "userSetupComplete = %s, ignoring panic gesture.",
+ "userSetupComplete = %s, ignoring emergency gesture.",
userSetupComplete));
}
return false;
}
if (DBG) {
Slog.d(TAG, String.format(
- "userSetupComplete = %s, performing panic gesture.",
+ "userSetupComplete = %s, performing emergency gesture.",
userSetupComplete));
}
StatusBarManagerInternal service = LocalServices.getService(
@@ -558,7 +556,7 @@
registerContentObservers();
updateCameraRegistered();
updateCameraDoubleTapPowerEnabled();
- updatePanicButtonGestureEnabled();
+ updateEmergencyGestureEnabled();
}
}
};
@@ -568,7 +566,7 @@
if (userId == mUserId) {
updateCameraRegistered();
updateCameraDoubleTapPowerEnabled();
- updatePanicButtonGestureEnabled();
+ updateEmergencyGestureEnabled();
}
}
};
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index c061137..c23f1ca 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.os.Build;
import android.os.Process;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -44,7 +45,7 @@
*
* @hide
*/
-public class SystemServerInitThreadPool {
+public final class SystemServerInitThreadPool implements Dumpable {
private static final String TAG = SystemServerInitThreadPool.class.getSimpleName();
private static final int SHUTDOWN_TIMEOUT_MILLIS = 20000;
private static final boolean IS_DEBUGGABLE = Build.IS_DEBUGGABLE;
@@ -53,6 +54,7 @@
@GuardedBy("LOCK")
private static SystemServerInitThreadPool sInstance;
+ private final int mSize; // used by dump() only
private final ExecutorService mService;
@GuardedBy("mPendingTasks")
@@ -62,9 +64,9 @@
private boolean mShutDown;
private SystemServerInitThreadPool() {
- final int size = Runtime.getRuntime().availableProcessors();
- Slog.i(TAG, "Creating instance with " + size + " threads");
- mService = ConcurrentUtils.newFixedThreadPool(size,
+ mSize = Runtime.getRuntime().availableProcessors();
+ Slog.i(TAG, "Creating instance with " + mSize + " threads");
+ mService = ConcurrentUtils.newFixedThreadPool(mSize,
"system-server-init-thread", Process.THREAD_PRIORITY_FOREGROUND);
}
@@ -123,11 +125,13 @@
*
* @throws IllegalStateException if it has been started already without being shut down yet.
*/
- static void start() {
+ static SystemServerInitThreadPool start() {
+ SystemServerInitThreadPool instance;
synchronized (LOCK) {
Preconditions.checkState(sInstance == null, TAG + " already started");
- sInstance = new SystemServerInitThreadPool();
+ instance = sInstance = new SystemServerInitThreadPool();
}
+ return instance;
}
/**
@@ -190,4 +194,22 @@
ActivityManagerService.dumpStackTraces(pids, null, null,
Watchdog.getInterestingNativePids(), null);
}
+
+ @Override
+ public void dump(IndentingPrintWriter pw, String[] args) {
+ synchronized (LOCK) {
+ pw.printf("has instance: %b\n", (sInstance != null));
+ }
+ pw.printf("number of threads: %d\n", mSize);
+ pw.printf("service: %s\n", mService);
+ synchronized (mPendingTasks) {
+ pw.printf("is shutdown: %b\n", mShutDown);
+ final int pendingTasks = mPendingTasks.size();
+ if (pendingTasks == 0) {
+ pw.println("no pending tasks");
+ } else {
+ pw.printf("%d pending tasks: %s\n", pendingTasks, mPendingTasks);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index 84d01ec..6c81de6 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -200,21 +200,21 @@
/**
* @hide
*/
- public void dump(@NonNull StringBuilder builder) {
- builder.append(getUserIdentifier());
+ public void dump(@NonNull PrintWriter pw) {
+ pw.print(getUserIdentifier());
if (!isFull() && !isManagedProfile()) return;
- builder.append('(');
+ pw.print('(');
boolean addComma = false;
if (isFull()) {
- builder.append("full");
+ pw.print("full");
}
if (isManagedProfile()) {
- if (addComma) builder.append(',');
- builder.append("mp");
+ if (addComma) pw.print(',');
+ pw.print("mp");
}
- builder.append(')');
+ pw.print(')');
}
}
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index ff2661b..71a1821 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -27,6 +27,7 @@
import android.os.UserManagerInternal;
import android.util.ArrayMap;
import android.util.EventLog;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
@@ -49,7 +50,7 @@
*
* {@hide}
*/
-public final class SystemServiceManager {
+public final class SystemServiceManager implements Dumpable {
private static final String TAG = SystemServiceManager.class.getSimpleName();
private static final boolean DEBUG = false;
private static final int SERVICE_CALL_WARN_TIME_MS = 50;
@@ -489,31 +490,39 @@
return sSystemDir;
}
- /**
- * Outputs the state of this manager to the System log.
- */
- public void dump() {
- StringBuilder builder = new StringBuilder();
- builder.append("Current phase: ").append(mCurrentPhase).append('\n');
- builder.append("Services:\n");
- final int startedLen = mServices.size();
- for (int i = 0; i < startedLen; i++) {
- final SystemService service = mServices.get(i);
- builder.append("\t")
- .append(service.getClass().getSimpleName())
- .append("\n");
- }
+ @Override
+ public void dump(IndentingPrintWriter pw, String[] args) {
+ pw.printf("Current phase: %d\n", mCurrentPhase);
synchronized (mTargetUsers) {
- builder.append("Current user: ").append(mCurrentUser).append('\n');
- builder.append("Target users: ");
- final int targetUsersSize = mTargetUsers.size();
- for (int i = 0; i < targetUsersSize; i++) {
- mTargetUsers.valueAt(i).dump(builder);
- if (i != targetUsersSize - 1) builder.append(',');
+ if (mCurrentUser != null) {
+ pw.print("Current user: "); mCurrentUser.dump(pw); pw.println();
+ } else {
+ pw.println("Current user not set!");
}
- builder.append('\n');
- }
- Slog.e(TAG, builder.toString());
+ final int targetUsersSize = mTargetUsers.size();
+ if (targetUsersSize > 0) {
+ pw.printf("%d target users: ", targetUsersSize);
+ for (int i = 0; i < targetUsersSize; i++) {
+ mTargetUsers.valueAt(i).dump(pw);
+ if (i != targetUsersSize - 1) pw.print(", ");
+ }
+ pw.println();
+ } else {
+ pw.println("No target users");
+ }
+ }
+ final int startedLen = mServices.size();
+ if (startedLen > 0) {
+ pw.printf("%d started services:\n", startedLen);
+ pw.increaseIndent();
+ for (int i = 0; i < startedLen; i++) {
+ final SystemService service = mServices.get(i);
+ pw.println(service.getClass().getCanonicalName());
+ }
+ pw.decreaseIndent();
+ } else {
+ pw.println("No started services");
+ }
}
}
diff --git a/services/core/java/com/android/server/VibratorManagerService.java b/services/core/java/com/android/server/VibratorManagerService.java
index 2f35da7..356cd0c 100644
--- a/services/core/java/com/android/server/VibratorManagerService.java
+++ b/services/core/java/com/android/server/VibratorManagerService.java
@@ -16,12 +16,15 @@
package com.android.server;
+import android.annotation.Nullable;
import android.content.Context;
+import android.os.CombinedVibrationEffect;
import android.os.IBinder;
import android.os.IVibratorManagerService;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.ShellCommand;
+import android.os.VibrationAttributes;
import com.android.internal.annotations.VisibleForTesting;
@@ -66,6 +69,17 @@
return Arrays.copyOf(mVibratorIds, mVibratorIds.length);
}
+ @Override // Binder call
+ public void vibrate(int uid, String opPkg, CombinedVibrationEffect effect,
+ @Nullable VibrationAttributes attrs, String reason, IBinder token) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override // Binder call
+ public void cancelVibrate(IBinder token) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback cb, ResultReceiver resultReceiver) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 63f7d44..112814c69 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -208,6 +208,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.SELinuxUtil;
import android.content.pm.ServiceInfo;
+import android.content.pm.TestUtilityService;
import android.content.pm.UserInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -339,6 +340,7 @@
import com.android.server.ThreadPriorityBooster;
import com.android.server.UserspaceRebootLogger;
import com.android.server.Watchdog;
+import com.android.server.am.LowMemDetector.MemFactor;
import com.android.server.appop.AppOpsService;
import com.android.server.compat.PlatformCompat;
import com.android.server.contentcapture.ContentCaptureManagerInternal;
@@ -1311,6 +1313,7 @@
PackageManagerInternal mPackageManagerInt;
PermissionManagerServiceInternal mPermissionManagerInt;
+ private TestUtilityService mTestUtilityService;
/**
* Whether to force background check on all apps (for battery saver) or not.
@@ -5784,6 +5787,14 @@
return mPermissionManagerInt;
}
+ private TestUtilityService getTestUtilityServiceLocked() {
+ if (mTestUtilityService == null) {
+ mTestUtilityService =
+ LocalServices.getService(TestUtilityService.class);
+ }
+ return mTestUtilityService;
+ }
+
@Override
public void appNotResponding(final String reason) {
final int callingPid = Binder.getCallingPid();
@@ -7595,6 +7606,10 @@
eventType, r, processName, null, null, null, null, null, null, crashInfo);
mAppErrors.crashApplication(r, crashInfo);
+ // Notify package manager service to possibly update package state
+ if (r != null && r.info != null && r.info.packageName != null) {
+ mPackageManagerInt.notifyPackageCrashOrAnr(r.info.packageName);
+ }
}
public void handleApplicationStrictModeViolation(
@@ -8210,13 +8225,25 @@
}
@Override
- public int getMemoryTrimLevel() {
+ public @MemFactor int getMemoryTrimLevel() {
enforceNotIsolatedCaller("getMyMemoryState");
synchronized (this) {
return mAppProfiler.getLastMemoryLevelLocked();
}
}
+ void setMemFactorOverride(@MemFactor int level) {
+ synchronized (this) {
+ if (level == mAppProfiler.getLastMemoryLevelLocked()) {
+ return;
+ }
+
+ mAppProfiler.setMemFactorOverrideLocked(level);
+ // Kick off an oom adj update since we forced a mem factor update.
+ updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+ }
+ }
+
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
@@ -16590,6 +16617,11 @@
public boolean isPendingTopUid(int uid) {
return mPendingStartActivityUids.isPendingTopUid(uid);
}
+
+ @Override
+ public Intent getIntentForIntentSender(IIntentSender sender) {
+ return ActivityManagerService.this.getIntentForIntentSender(sender);
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
@@ -17334,11 +17366,12 @@
/**
* Holds the AM lock for the specified amount of milliseconds.
* Intended for use by the tests that need to imitate lock contention.
- * Requires permission identity of the shell UID.
+ * The token should be obtained by
+ * {@link android.content.pm.PackageManager#getHoldLockToken()}.
*/
@Override
- public void holdLock(int durationMs) {
- enforceCallingPermission(Manifest.permission.INJECT_EVENTS, "holdLock");
+ public void holdLock(IBinder token, int durationMs) {
+ getTestUtilityServiceLocked().verifyHoldLockToken(token);
synchronized (this) {
SystemClock.sleep(durationMs);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 31e7106..e3c071f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -25,6 +25,12 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.INVALID_DISPLAY;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+import static com.android.server.am.LowMemDetector.ADJ_MEM_FACTOR_NOTHING;
+
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
@@ -92,6 +98,7 @@
import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.internal.util.HexDump;
import com.android.internal.util.MemInfoReader;
+import com.android.server.am.LowMemDetector.MemFactor;
import com.android.server.compat.PlatformCompat;
import java.io.BufferedReader;
@@ -309,6 +316,8 @@
return runCompat(pw);
case "refresh-settings-cache":
return runRefreshSettingsCache();
+ case "memory-factor":
+ return runMemoryFactor(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -3014,6 +3023,81 @@
return -1;
}
+ private int runSetMemoryFactor(PrintWriter pw) throws RemoteException {
+ final String levelArg = getNextArgRequired();
+ @MemFactor int level = ADJ_MEM_FACTOR_NOTHING;
+ switch (levelArg) {
+ case "NORMAL":
+ level = ADJ_MEM_FACTOR_NORMAL;
+ break;
+ case "MODERATE":
+ level = ADJ_MEM_FACTOR_MODERATE;
+ break;
+ case "LOW":
+ level = ADJ_MEM_FACTOR_LOW;
+ break;
+ case "CRITICAL":
+ level = ADJ_MEM_FACTOR_CRITICAL;
+ break;
+ default:
+ try {
+ level = Integer.parseInt(levelArg);
+ } catch (NumberFormatException e) {
+ }
+ if (level < ADJ_MEM_FACTOR_NORMAL || level > ADJ_MEM_FACTOR_CRITICAL) {
+ getErrPrintWriter().println("Error: Unknown level option: " + levelArg);
+ return -1;
+ }
+ }
+ mInternal.setMemFactorOverride(level);
+ return 0;
+ }
+
+ private int runShowMemoryFactor(PrintWriter pw) throws RemoteException {
+ final @MemFactor int level = mInternal.getMemoryTrimLevel();
+ switch (level) {
+ case ADJ_MEM_FACTOR_NOTHING:
+ pw.println("<UNKNOWN>");
+ break;
+ case ADJ_MEM_FACTOR_NORMAL:
+ pw.println("NORMAL");
+ break;
+ case ADJ_MEM_FACTOR_MODERATE:
+ pw.println("MODERATE");
+ break;
+ case ADJ_MEM_FACTOR_LOW:
+ pw.println("LOW");
+ break;
+ case ADJ_MEM_FACTOR_CRITICAL:
+ pw.println("CRITICAL");
+ break;
+ }
+ pw.flush();
+ return 0;
+ }
+
+ private int runResetMemoryFactor(PrintWriter pw) throws RemoteException {
+ mInternal.setMemFactorOverride(ADJ_MEM_FACTOR_NOTHING);
+ return 0;
+ }
+
+ private int runMemoryFactor(PrintWriter pw) throws RemoteException {
+ mInternal.enforceCallingPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
+ "runMemoryFactor()");
+
+ final String op = getNextArgRequired();
+ switch (op) {
+ case "set":
+ return runSetMemoryFactor(pw);
+ case "show":
+ return runShowMemoryFactor(pw);
+ case "reset":
+ return runResetMemoryFactor(pw);
+ default:
+ getErrPrintWriter().println("Error: unknown command '" + op + "'");
+ return -1;
+ }
+ }
private Resources getResources(PrintWriter pw) throws RemoteException {
// system resources does not contain all the device configuration, construct it manually.
@@ -3334,6 +3418,13 @@
pw.println(" Removes all existing overrides for all changes for ");
pw.println(" <PACKAGE_NAME> (back to default behaviour).");
pw.println(" It kills <PACKAGE_NAME> (to allow the toggle to take effect).");
+ pw.println(" memory-factor [command] [...]: sub-commands for overriding memory pressure factor");
+ pw.println(" set <NORMAL|MODERATE|LOW|CRITICAL>");
+ pw.println(" Overrides memory pressure factor. May also supply a raw int level");
+ pw.println(" show");
+ pw.println(" Shows the existing memory pressure factor");
+ pw.println(" reset");
+ pw.println(" Removes existing override for memory pressure factor");
pw.println();
Intent.printIntentArgsHelp(pw, "");
}
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 31ffb35..5e59a35 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -20,11 +20,16 @@
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.Process.FIRST_APPLICATION_UID;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.LowMemDetector.ADJ_MEM_FACTOR_NOTHING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import android.annotation.BroadcastBehavior;
@@ -72,6 +77,7 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.MemInfoReader;
+import com.android.server.am.LowMemDetector.MemFactor;
import com.android.server.am.ProcessList.ProcStateMemTracker;
import com.android.server.utils.PriorityDump;
@@ -202,7 +208,10 @@
* processes are going away for other reasons.
*/
@GuardedBy("mService")
- private int mLastMemoryLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+ private @MemFactor int mLastMemoryLevel = ADJ_MEM_FACTOR_NORMAL;
+
+ @GuardedBy("mService")
+ private @MemFactor int mMemFactorOverride = ADJ_MEM_FACTOR_NOTHING;
/**
* The last total number of process we have, to determine if changes actually look
@@ -851,7 +860,7 @@
@GuardedBy("mService")
boolean isLastMemoryLevelNormal() {
- return mLastMemoryLevel <= ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+ return mLastMemoryLevel <= ADJ_MEM_FACTOR_NORMAL;
}
@GuardedBy("mService")
@@ -868,6 +877,11 @@
}
@GuardedBy("mService")
+ void setMemFactorOverrideLocked(@MemFactor int factor) {
+ mMemFactorOverride = factor;
+ }
+
+ @GuardedBy("mService")
boolean updateLowMemStateLocked(int numCached, int numEmpty, int numTrimming) {
final int numOfLru = mService.mProcessList.getLruSizeLocked();
final long now = SystemClock.uptimeMillis();
@@ -885,28 +899,32 @@
&& numEmpty <= mService.mConstants.CUR_TRIM_EMPTY_PROCESSES) {
final int numCachedAndEmpty = numCached + numEmpty;
if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
- memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
+ memFactor = ADJ_MEM_FACTOR_CRITICAL;
} else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
- memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW;
+ memFactor = ADJ_MEM_FACTOR_LOW;
} else {
- memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE;
+ memFactor = ADJ_MEM_FACTOR_MODERATE;
}
} else {
- memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+ memFactor = ADJ_MEM_FACTOR_NORMAL;
}
}
// We always allow the memory level to go up (better). We only allow it to go
// down if we are in a state where that is allowed, *and* the total number of processes
// has gone down since last time.
if (DEBUG_OOM_ADJ) {
- Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor
+ Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor + " override=" + mMemFactorOverride
+ " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel
+ " numProcs=" + mService.mProcessList.getLruSizeLocked()
+ " last=" + mLastNumProcesses);
}
+ boolean override;
+ if (override = (mMemFactorOverride != ADJ_MEM_FACTOR_NOTHING)) {
+ memFactor = mMemFactorOverride;
+ }
if (memFactor > mLastMemoryLevel) {
- if (!mAllowLowerMemLevel
- || mService.mProcessList.getLruSizeLocked() >= mLastNumProcesses) {
+ if (!override && (!mAllowLowerMemLevel
+ || mService.mProcessList.getLruSizeLocked() >= mLastNumProcesses)) {
memFactor = mLastMemoryLevel;
if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "Keeping last mem factor!");
}
@@ -924,17 +942,17 @@
mService.mAtmInternal == null || !mService.mAtmInternal.isSleeping(), now);
trackerMemFactor = mService.mProcessStats.getMemFactorLocked();
}
- if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) {
+ if (memFactor != ADJ_MEM_FACTOR_NORMAL) {
if (mLowRamStartTime == 0) {
mLowRamStartTime = now;
}
int step = 0;
int fgTrimLevel;
switch (memFactor) {
- case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
+ case ADJ_MEM_FACTOR_CRITICAL:
fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
break;
- case ProcessStats.ADJ_MEM_FACTOR_LOW:
+ case ADJ_MEM_FACTOR_LOW:
fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
break;
default:
@@ -947,7 +965,7 @@
if (mService.mAtmInternal.getPreviousProcess() != null) minFactor++;
if (factor < minFactor) factor = minFactor;
int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
- for (int i = numOfLru - 1; i >= 0; i--) {
+ for (int i = 0; i < numOfLru; i++) {
ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
if (allChanged || app.procStateChanged) {
mService.setProcessTrackerStateLocked(app, trackerMemFactor, now);
@@ -1032,7 +1050,7 @@
mLowRamTimeSinceLastIdle += now - mLowRamStartTime;
mLowRamStartTime = 0;
}
- for (int i = numOfLru - 1; i >= 0; i--) {
+ for (int i = 0; i < numOfLru; i++) {
ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
if (allChanged || app.procStateChanged) {
mService.setProcessTrackerStateLocked(app, trackerMemFactor, now);
@@ -1622,16 +1640,16 @@
@GuardedBy("mService")
void dumpLastMemoryLevelLocked(PrintWriter pw) {
switch (mLastMemoryLevel) {
- case ProcessStats.ADJ_MEM_FACTOR_NORMAL:
+ case ADJ_MEM_FACTOR_NORMAL:
pw.println("normal)");
break;
- case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
+ case ADJ_MEM_FACTOR_MODERATE:
pw.println("moderate)");
break;
- case ProcessStats.ADJ_MEM_FACTOR_LOW:
+ case ADJ_MEM_FACTOR_LOW:
pw.println("low)");
break;
- case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
+ case ADJ_MEM_FACTOR_CRITICAL:
pw.println("critical)");
break;
default:
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index b6010d9..93dd1aa 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -22,6 +22,7 @@
import android.net.wifi.WifiManager;
import android.os.BatteryStats;
import android.os.Bundle;
+import android.os.OutcomeReceiver;
import android.os.Parcelable;
import android.os.Process;
import android.os.ServiceManager;
@@ -39,6 +40,7 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.function.pooled.PooledLambda;
+import java.util.concurrent.ExecutionException;
import libcore.util.EmptyArray;
import java.util.concurrent.CompletableFuture;
@@ -405,7 +407,7 @@
// We will request data from external processes asynchronously, and wait on a timeout.
SynchronousResultReceiver wifiReceiver = null;
SynchronousResultReceiver bluetoothReceiver = null;
- SynchronousResultReceiver modemReceiver = null;
+ CompletableFuture<ModemActivityInfo> modemFuture = CompletableFuture.completedFuture(null);
boolean railUpdated = false;
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) {
@@ -460,8 +462,22 @@
}
if (mTelephony != null) {
- modemReceiver = new SynchronousResultReceiver("telephony");
- mTelephony.requestModemActivityInfo(modemReceiver);
+ CompletableFuture<ModemActivityInfo> temp = new CompletableFuture<>();
+ mTelephony.requestModemActivityInfo(Runnable::run,
+ new OutcomeReceiver<ModemActivityInfo,
+ TelephonyManager.ModemActivityInfoException>() {
+ @Override
+ public void onResult(ModemActivityInfo result) {
+ temp.complete(result);
+ }
+
+ @Override
+ public void onError(TelephonyManager.ModemActivityInfoException e) {
+ Slog.w(TAG, "error reading modem stats:" + e);
+ temp.complete(null);
+ }
+ });
+ modemFuture = temp;
}
if (!railUpdated) {
synchronized (mStats) {
@@ -472,7 +488,15 @@
final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
final BluetoothActivityEnergyInfo bluetoothInfo = awaitControllerInfo(bluetoothReceiver);
- final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
+ ModemActivityInfo modemInfo = null;
+ try {
+ modemInfo = modemFuture.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
+ TimeUnit.MILLISECONDS);
+ } catch (TimeoutException | InterruptedException e) {
+ Slog.w(TAG, "timeout or interrupt reading modem stats: " + e);
+ } catch (ExecutionException e) {
+ Slog.w(TAG, "exception reading modem stats: " + e.getCause());
+ }
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
final long elapsedRealtimeUs = elapsedRealtime * 1000;
@@ -523,11 +547,7 @@
}
if (modemInfo != null) {
- if (modemInfo.isValid()) {
- mStats.updateMobileRadioState(modemInfo, elapsedRealtime, uptime);
- } else {
- Slog.w(TAG, "modem info is invalid: " + modemInfo);
- }
+ mStats.updateMobileRadioState(modemInfo, elapsedRealtime, uptime);
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 6f706ac..29431b1 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1760,7 +1760,7 @@
public void noteModemControllerActivity(final ModemActivityInfo info) {
enforceCallingPermission();
- if (info == null || !info.isValid()) {
+ if (info == null) {
Slog.e(TAG, "invalid modem data given: " + info);
return;
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 57f8112..f1c5915 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -18,6 +18,7 @@
import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
+import static android.text.TextUtils.formatSimple;
import static com.android.server.am.ActivityManagerDebugConfig.*;
@@ -1892,7 +1893,7 @@
}
private String createBroadcastTraceTitle(BroadcastRecord record, int state) {
- return String.format("Broadcast %s from %s (%s) %s",
+ return formatSimple("Broadcast %s from %s (%s) %s",
state == BroadcastRecord.DELIVERY_PENDING ? "in queue" : "dispatched",
record.callerPackage == null ? "" : record.callerPackage,
record.callerApp == null ? "process unknown" : record.callerApp.toShortString(),
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 8112bb8..cd0d5b4 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -777,27 +777,27 @@
long freezeTime = app.freezeUnfreezeTime;
try {
+ freezeBinder(app.pid, false);
+ } catch (RuntimeException e) {
+ Slog.e(TAG_AM, "Unable to unfreeze binder for " + app.pid + " " + app.processName
+ + ". Killing it");
+ app.kill("Unable to unfreeze",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
+ return;
+ }
+
+ try {
Process.setProcessFrozen(app.pid, app.uid, false);
app.freezeUnfreezeTime = SystemClock.uptimeMillis();
app.frozen = false;
} catch (Exception e) {
Slog.e(TAG_AM, "Unable to unfreeze " + app.pid + " " + app.processName
- + ". Any related user experience might be hanged.");
+ + ". This might cause inconsistency or UI hangs.");
}
if (!app.frozen) {
- try {
- freezeBinder(app.pid, false);
- } catch (RuntimeException e) {
- Slog.e(TAG_AM, "Unable to unfreeze binder for " + app.pid + " " + app.processName
- + ". Killing it");
- app.kill("Unable to unfreeze",
- ApplicationExitInfo.REASON_OTHER,
- ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
- return;
- }
-
if (DEBUG_FREEZER) {
Slog.d(TAG_AM, "sync unfroze " + app.pid + " " + app.processName);
}
@@ -1110,14 +1110,6 @@
return;
}
- try {
- freezeBinder(pid, true);
- } catch (RuntimeException e) {
- // TODO: it might be preferable to kill the target pid in this case
- Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
- return;
- }
-
if (pid == 0 || proc.frozen) {
// Already frozen or not a real process, either one being
// launched or one being killed
@@ -1146,6 +1138,15 @@
EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name);
+ try {
+ freezeBinder(pid, true);
+ } catch (RuntimeException e) {
+ Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
+ proc.kill("Unable to freeze binder interface",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
+ }
+
// See above for why we're not taking mPhenotypeFlagLock here
if (mRandom.nextFloat() < mFreezerStatsdSampleRate) {
FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED,
diff --git a/services/core/java/com/android/server/am/ContentProviderConnection.java b/services/core/java/com/android/server/am/ContentProviderConnection.java
index df8bff2..be49ce4 100644
--- a/services/core/java/com/android/server/am/ContentProviderConnection.java
+++ b/services/core/java/com/android/server/am/ContentProviderConnection.java
@@ -265,4 +265,13 @@
return mUnstableCount;
}
}
+
+ /**
+ * Returns the total number of stable and unstable references.
+ */
+ int totalRefCount() {
+ synchronized (mLock) {
+ return mStableCount + mUnstableCount;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 950f0a0..34256a0 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -62,6 +62,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.server.RescueParty;
@@ -261,7 +262,8 @@
// doesn't kill our process.
Slog.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
+ " is crashing; detaching " + r);
- boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
+ boolean lastRef = decProviderCountLocked(conn, cpr, token, stable,
+ false, false);
if (!lastRef) {
// This wasn't the last ref our process had on
// the provider... we will be killed during cleaning up, bail.
@@ -697,10 +699,7 @@
if (conn == null) {
throw new NullPointerException("connection is null");
}
- if (decProviderCountLocked(conn, null, null, stable)) {
- mService.updateOomAdjLocked(conn.provider.proc,
- OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
- }
+ decProviderCountLocked(conn, null, null, stable, true, true);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -1275,30 +1274,56 @@
@GuardedBy("mService")
private boolean decProviderCountLocked(ContentProviderConnection conn,
- ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
+ ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable,
+ boolean enforceDelay, boolean updateOomAdj) {
if (conn == null) {
cpr.removeExternalProcessHandleLocked(externalProcessToken);
return false;
}
- if (conn.decrementCount(stable) != 0) {
+
+ if (conn.totalRefCount() > 1) {
+ conn.decrementCount(stable);
return false;
}
+ if (enforceDelay) {
+ // delay the removal of the provider for 5 seconds - this optimizes for those cases
+ // where providers are released and then quickly re-acquired, causing lots of churn.
+ BackgroundThread.getHandler().postDelayed(() -> {
+ handleProviderRemoval(conn, stable, updateOomAdj);
+ }, 5 * 1000);
+ } else {
+ handleProviderRemoval(conn, stable, updateOomAdj);
+ }
+ return true;
+ }
- cpr = conn.provider;
- conn.stopAssociation();
- cpr.connections.remove(conn);
- conn.client.conProviders.remove(conn);
- if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
- // The client is more important than last activity -- note the time this
- // is happening, so we keep the old provider process around a bit as last
- // activity to avoid thrashing it.
- if (cpr.proc != null) {
- cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
+ private void handleProviderRemoval(ContentProviderConnection conn, boolean stable,
+ boolean updateOomAdj) {
+ synchronized (mService) {
+ // if the proc was already killed or this is not the last reference, simply exit.
+ if (conn == null || conn.provider == null || conn.decrementCount(stable) != 0) {
+ return;
+ }
+
+ final ContentProviderRecord cpr = conn.provider;
+ conn.stopAssociation();
+ cpr.connections.remove(conn);
+ conn.client.conProviders.remove(conn);
+ if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+ // The client is more important than last activity -- note the time this
+ // is happening, so we keep the old provider process around a bit as last
+ // activity to avoid thrashing it.
+ if (cpr.proc != null) {
+ cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
+ }
+ }
+ mService.stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
+ cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
+ if (updateOomAdj) {
+ mService.updateOomAdjLocked(conn.provider.proc,
+ OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
}
}
- mService.stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
- cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
- return true;
}
/**
diff --git a/services/core/java/com/android/server/am/LowMemDetector.java b/services/core/java/com/android/server/am/LowMemDetector.java
index e82a207..8f79133 100644
--- a/services/core/java/com/android/server/am/LowMemDetector.java
+++ b/services/core/java/com/android/server/am/LowMemDetector.java
@@ -16,8 +16,19 @@
package com.android.server.am;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_NOTHING;
+
+import android.annotation.IntDef;
+
import com.android.internal.annotations.GuardedBy;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Detects low memory using PSI.
*
@@ -32,13 +43,20 @@
private final Object mPressureStateLock = new Object();
@GuardedBy("mPressureStateLock")
- private int mPressureState = MEM_PRESSURE_NONE;
+ private int mPressureState = ADJ_MEM_FACTOR_NORMAL;
+
+ public static final int ADJ_MEM_FACTOR_NOTHING = ADJ_NOTHING;
/* getPressureState return values */
- public static final int MEM_PRESSURE_NONE = 0;
- public static final int MEM_PRESSURE_LOW = 1;
- public static final int MEM_PRESSURE_MEDIUM = 2;
- public static final int MEM_PRESSURE_HIGH = 3;
+ @IntDef(prefix = { "ADJ_MEM_FACTOR_" }, value = {
+ ADJ_MEM_FACTOR_NOTHING,
+ ADJ_MEM_FACTOR_NORMAL,
+ ADJ_MEM_FACTOR_MODERATE,
+ ADJ_MEM_FACTOR_LOW,
+ ADJ_MEM_FACTOR_CRITICAL,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MemFactor{}
LowMemDetector(ActivityManagerService am) {
mAm = am;
@@ -62,7 +80,7 @@
* there should be conversion performed here to translate pressure state
* into memFactor.
*/
- public int getMemFactor() {
+ public @MemFactor int getMemFactor() {
synchronized (mPressureStateLock) {
return mPressureState;
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 53c6758..ccdd6a7 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1764,6 +1764,12 @@
makeAppNotRespondingLocked(activityShortComponentName,
annotation != null ? "ANR " + annotation : "ANR", info.toString());
+ // Notify package manager service to possibly update package state
+ if (aInfo != null && aInfo.packageName != null) {
+ mService.getPackageManagerInternalLocked().notifyPackageCrashOrAnr(
+ aInfo.packageName);
+ }
+
// mUiHandler can be null if the AMS is constructed with injector only. This will only
// happen in tests.
if (mService.mUiHandler != null) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 662d8d5..cfc58a5 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1078,7 +1078,7 @@
// Restore call state
synchronized (mDeviceBroker.mSetModeLock) {
- if (AudioSystem.setPhoneState(mMode, getModeOwnerUid())
+ if (mAudioSystem.setPhoneState(mMode, getModeOwnerUid())
== AudioSystem.AUDIO_STATUS_OK) {
mModeLogger.log(new AudioEventLogger.StringEvent(
"onAudioServerDied causes setPhoneState(" + AudioSystem.modeToString(mMode)
@@ -1165,7 +1165,7 @@
HashMap<Integer, Integer> allowedCapturePolicies =
mPlaybackMonitor.getAllAllowedCapturePolicies();
for (HashMap.Entry<Integer, Integer> entry : allowedCapturePolicies.entrySet()) {
- int result = AudioSystem.setAllowedCapturePolicy(
+ int result = mAudioSystem.setAllowedCapturePolicy(
entry.getKey(),
AudioAttributes.capturePolicyToFlags(entry.getValue(), 0x0));
if (result != AudioSystem.AUDIO_STATUS_OK) {
@@ -2112,7 +2112,7 @@
protected @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesInt(
@NonNull AudioAttributes attributes) {
Objects.requireNonNull(attributes);
- return AudioSystem.getDevicesForAttributes(attributes);
+ return mAudioSystem.getDevicesForAttributes(attributes);
}
/** Indicates no special treatment in the handling of the volume adjustement */
@@ -2219,7 +2219,7 @@
|| maybeActiveStreamType == AudioSystem.STREAM_NOTIFICATION) {
activeForReal = wasStreamActiveRecently(maybeActiveStreamType, 0);
} else {
- activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0);
+ activeForReal = mAudioSystem.isStreamActive(maybeActiveStreamType, 0);
}
if (activeForReal || mVolumeControlStream == -1) {
streamType = maybeActiveStreamType;
@@ -2916,7 +2916,7 @@
int streamType = getHearingAidStreamType(newMode);
final Set<Integer> deviceTypes = AudioSystem.generateAudioDeviceTypesSet(
- AudioSystem.getDevicesForStream(streamType));
+ mAudioSystem.getDevicesForStream(streamType));
final Set<Integer> absVolumeMultiModeCaseDevices = AudioSystem.intersectionAudioDeviceTypes(
mAbsVolumeMultiModeCaseDevices, deviceTypes);
if (absVolumeMultiModeCaseDevices.isEmpty()) {
@@ -4123,7 +4123,7 @@
if (actualMode != mMode) {
final long identity = Binder.clearCallingIdentity();
- status = AudioSystem.setPhoneState(actualMode, getModeOwnerUid());
+ status = mAudioSystem.setPhoneState(actualMode, getModeOwnerUid());
Binder.restoreCallingIdentity(identity);
if (status == AudioSystem.AUDIO_STATUS_OK) {
if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + actualMode); }
@@ -4597,7 +4597,7 @@
caller,
MUSIC_ACTIVE_POLL_PERIOD_MS);
int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(device);
- if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)
+ if (mAudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)
&& (index > safeMediaVolumeIndex(device))) {
// Approximate cumulative active music time
mMusicActiveMs += MUSIC_ACTIVE_POLL_PERIOD_MS;
@@ -4981,8 +4981,8 @@
* in the last "delay_ms" ms.
*/
private boolean wasStreamActiveRecently(int stream, int delay_ms) {
- return AudioSystem.isStreamActive(stream, delay_ms)
- || AudioSystem.isStreamActiveRemotely(stream, delay_ms);
+ return mAudioSystem.isStreamActive(stream, delay_ms)
+ || mAudioSystem.isStreamActiveRemotely(stream, delay_ms);
}
private int getActiveStreamType(int suggestedStreamType) {
@@ -4994,7 +4994,7 @@
switch (mPlatformType) {
case AudioSystem.PLATFORM_VOICE:
if (isInCommunication()) {
- if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
+ if (mAudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
== AudioSystem.FORCE_BT_SCO) {
// Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
return AudioSystem.STREAM_BLUETOOTH_SCO;
@@ -5031,7 +5031,7 @@
}
default:
if (isInCommunication()) {
- if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
+ if (mAudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
== AudioSystem.FORCE_BT_SCO) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
return AudioSystem.STREAM_BLUETOOTH_SCO;
@@ -5039,30 +5039,30 @@
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL");
return AudioSystem.STREAM_VOICE_CALL;
}
- } else if (AudioSystem.isStreamActive(
+ } else if (mAudioSystem.isStreamActive(
AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
return AudioSystem.STREAM_NOTIFICATION;
- } else if (AudioSystem.isStreamActive(
+ } else if (mAudioSystem.isStreamActive(
AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING");
return AudioSystem.STREAM_RING;
} else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
- if (AudioSystem.isStreamActive(
+ if (mAudioSystem.isStreamActive(
AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
return AudioSystem.STREAM_NOTIFICATION;
- } else if (AudioSystem.isStreamActive(
+ }
+ if (mAudioSystem.isStreamActive(
AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING");
return AudioSystem.STREAM_RING;
- } else {
- if (DEBUG_VOL) {
- Log.v(TAG, "getActiveStreamType: Forcing DEFAULT_VOL_STREAM_NO_PLAYBACK("
- + DEFAULT_VOL_STREAM_NO_PLAYBACK + ") b/c default");
- }
- return DEFAULT_VOL_STREAM_NO_PLAYBACK;
}
+ if (DEBUG_VOL) {
+ Log.v(TAG, "getActiveStreamType: Forcing DEFAULT_VOL_STREAM_NO_PLAYBACK("
+ + DEFAULT_VOL_STREAM_NO_PLAYBACK + ") b/c default");
+ }
+ return DEFAULT_VOL_STREAM_NO_PLAYBACK;
}
break;
}
@@ -5477,7 +5477,7 @@
&& DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.contains(newDevice)
&& mStreamStates[AudioSystem.STREAM_MUSIC].mIsMuted
&& mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(newDevice) != 0
- && (newDevice & AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC)) != 0) {
+ && (newDevice & mAudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC)) != 0) {
if (DEBUG_VOL) {
Log.i(TAG, String.format("onAccessoryPlugMediaUnmute unmuting device=%d [%s]",
newDevice, AudioSystem.getOutputDeviceName(newDevice)));
@@ -5945,7 +5945,7 @@
if (!mSystemServer.isPrivileged()) {
return AudioSystem.DEVICE_NONE;
}
- final int devices = AudioSystem.getDevicesForStream(mStreamType);
+ final int devices = mAudioSystem.getDevicesForStream(mStreamType);
if (devices == mObservedDevices) {
return devices;
}
@@ -6670,7 +6670,7 @@
.record();
sForceUseLogger.log(
new AudioServiceEvents.ForceUseEvent(useCase, config, eventSource));
- AudioSystem.setForceUse(useCase, config);
+ mAudioSystem.setForceUse(useCase, config);
}
break;
@@ -7993,7 +7993,7 @@
}
}
- public static class VolumeController {
+ public class VolumeController {
private static final String TAG = "VolumeController";
private IVolumeController mController;
@@ -8033,7 +8033,7 @@
if (resolvedStream == DEFAULT_VOL_STREAM_NO_PLAYBACK && mController != null) {
// never suppress media vol adjustement during media playback
if (resolvedStream == AudioSystem.STREAM_MUSIC
- && AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, mLongPressTimeout))
+ && mAudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, mLongPressTimeout))
{
// media is playing, adjust the volume right away
return false;
@@ -8071,7 +8071,7 @@
return binder(mController);
}
- private static IBinder binder(IVolumeController controller) {
+ private IBinder binder(IVolumeController controller) {
return controller == null ? null : controller.asBinder();
}
@@ -8800,7 +8800,7 @@
int flags = AudioAttributes.capturePolicyToFlags(capturePolicy, 0x0);
final long identity = Binder.clearCallingIdentity();
synchronized (mPlaybackMonitor) {
- int result = AudioSystem.setAllowedCapturePolicy(callingUid, flags);
+ int result = mAudioSystem.setAllowedCapturePolicy(callingUid, flags);
if (result == AudioSystem.AUDIO_STATUS_OK) {
mPlaybackMonitor.setAllowedCapturePolicy(callingUid, capturePolicy);
}
@@ -8943,7 +8943,7 @@
}
}
final long identity = Binder.clearCallingIdentity();
- AudioSystem.registerPolicyMixes(mMixes, false);
+ mAudioSystem.registerPolicyMixes(mMixes, false);
Binder.restoreCallingIdentity(identity);
synchronized (mAudioPolicies) {
mAudioPolicies.remove(mPolicyCallback.asBinder());
@@ -8986,24 +8986,24 @@
int addMixes(@NonNull ArrayList<AudioMix> mixes) {
// TODO optimize to not have to unregister the mixes already in place
synchronized (mMixes) {
- AudioSystem.registerPolicyMixes(mMixes, false);
+ mAudioSystem.registerPolicyMixes(mMixes, false);
this.add(mixes);
- return AudioSystem.registerPolicyMixes(mMixes, true);
+ return mAudioSystem.registerPolicyMixes(mMixes, true);
}
}
int removeMixes(@NonNull ArrayList<AudioMix> mixes) {
// TODO optimize to not have to unregister the mixes already in place
synchronized (mMixes) {
- AudioSystem.registerPolicyMixes(mMixes, false);
+ mAudioSystem.registerPolicyMixes(mMixes, false);
this.remove(mixes);
- return AudioSystem.registerPolicyMixes(mMixes, true);
+ return mAudioSystem.registerPolicyMixes(mMixes, true);
}
}
@AudioSystem.AudioSystemError int connectMixes() {
final long identity = Binder.clearCallingIdentity();
- int status = AudioSystem.registerPolicyMixes(mMixes, true);
+ int status = mAudioSystem.registerPolicyMixes(mMixes, true);
Binder.restoreCallingIdentity(identity);
return status;
}
@@ -9039,7 +9039,7 @@
@AudioSystem.AudioSystemError private int removeUidDeviceAffinitiesFromSystem(int uid) {
final long identity = Binder.clearCallingIdentity();
try {
- return AudioSystem.removeUidDeviceAffinities(uid);
+ return mAudioSystem.removeUidDeviceAffinities(uid);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -9049,7 +9049,7 @@
AudioDeviceArray deviceArray) {
final long identity = Binder.clearCallingIdentity();
try {
- return AudioSystem.setUidDeviceAffinities(uid, deviceArray.mDeviceTypes,
+ return mAudioSystem.setUidDeviceAffinities(uid, deviceArray.mDeviceTypes,
deviceArray.mDeviceAddresses);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -9091,7 +9091,7 @@
@UserIdInt int userId) {
final long identity = Binder.clearCallingIdentity();
try {
- return AudioSystem.removeUserIdDeviceAffinities(userId);
+ return mAudioSystem.removeUserIdDeviceAffinities(userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -9101,7 +9101,7 @@
@UserIdInt int userId, AudioDeviceArray deviceArray) {
final long identity = Binder.clearCallingIdentity();
try {
- return AudioSystem.setUserIdDeviceAffinities(userId, deviceArray.mDeviceTypes,
+ return mAudioSystem.setUserIdDeviceAffinities(userId, deviceArray.mDeviceTypes,
deviceArray.mDeviceAddresses);
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index ae64990..891c713 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -17,9 +17,12 @@
package com.android.server.audio;
import android.annotation.NonNull;
+import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioSystem;
+import android.media.audiopolicy.AudioMix;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -40,6 +43,25 @@
}
/**
+ * Same as {@link AudioSystem#getDevicesForStream(int)}
+ * @param stream a valid stream type
+ * @return a mask of device types
+ */
+ public int getDevicesForStream(int stream) {
+ return AudioSystem.getDevicesForStream(stream);
+ }
+
+ /**
+ * Same as {@link AudioSystem#getDevicesForAttributes(AudioAttributes)}
+ * @param attributes the attributes for which the routing is queried
+ * @return the devices that the stream with the given attributes would be routed to
+ */
+ public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
+ @NonNull AudioAttributes attributes) {
+ return AudioSystem.getDevicesForAttributes(attributes);
+ }
+
+ /**
* Same as {@link AudioSystem#setDeviceConnectionState(int, int, String, String, int)}
* @param device
* @param state
@@ -178,4 +200,104 @@
public boolean isStreamActive(int stream, int inPastMs) {
return AudioSystem.isStreamActive(stream, inPastMs);
}
+
+ /**
+ * Same as {@link AudioSystem#isStreamActiveRemotely(int, int)}
+ * @param stream
+ * @param inPastMs
+ * @return
+ */
+ public boolean isStreamActiveRemotely(int stream, int inPastMs) {
+ return AudioSystem.isStreamActiveRemotely(stream, inPastMs);
+ }
+
+ /**
+ * Same as {@link AudioSystem#setPhoneState(int, int)}
+ * @param state
+ * @param uid
+ * @return
+ */
+ public int setPhoneState(int state, int uid) {
+ return AudioSystem.setPhoneState(state, uid);
+ }
+
+ /**
+ * Same as {@link AudioSystem#setAllowedCapturePolicy(int, int)}
+ * @param uid
+ * @param flags
+ * @return
+ */
+ public int setAllowedCapturePolicy(int uid, int flags) {
+ return AudioSystem.setAllowedCapturePolicy(uid, flags);
+ }
+
+ /**
+ * Same as {@link AudioSystem#setForceUse(int, int)}
+ * @param usage
+ * @param config
+ * @return
+ */
+ public int setForceUse(int usage, int config) {
+ return AudioSystem.setForceUse(usage, config);
+ }
+
+ /**
+ * Same as {@link AudioSystem#getForceUse(int)}
+ * @param usage
+ * @return
+ */
+ public int getForceUse(int usage) {
+ return AudioSystem.getForceUse(usage);
+ }
+
+ /**
+ * Same as {@link AudioSystem#registerPolicyMixes(ArrayList, boolean)}
+ * @param mixes
+ * @param register
+ * @return
+ */
+ public int registerPolicyMixes(ArrayList<AudioMix> mixes, boolean register) {
+ return AudioSystem.registerPolicyMixes(mixes, register);
+ }
+
+ /**
+ * Same as {@link AudioSystem#setUidDeviceAffinities(int, int[], String[])}
+ * @param uid
+ * @param types
+ * @param addresses
+ * @return
+ */
+ public int setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
+ return AudioSystem.setUidDeviceAffinities(uid, types, addresses);
+ }
+
+ /**
+ * Same as {@link AudioSystem#removeUidDeviceAffinities(int)}
+ * @param uid
+ * @return
+ */
+ public int removeUidDeviceAffinities(int uid) {
+ return AudioSystem.removeUidDeviceAffinities(uid);
+ }
+
+ /**
+ * Same as {@link AudioSystem#setUserIdDeviceAffinities(int, int[], String[])}
+ * @param userId
+ * @param types
+ * @param addresses
+ * @return
+ */
+ public int setUserIdDeviceAffinities(int userId, @NonNull int[] types,
+ @NonNull String[] addresses) {
+ return AudioSystem.setUserIdDeviceAffinities(userId, types, addresses);
+ }
+
+ /**
+ * Same as {@link AudioSystem#removeUserIdDeviceAffinities(int)}
+ * @param userId
+ * @return
+ */
+ public int removeUserIdDeviceAffinities(int userId) {
+ return AudioSystem.removeUserIdDeviceAffinities(userId);
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/TEST_MAPPING b/services/core/java/com/android/server/biometrics/TEST_MAPPING
new file mode 100644
index 0000000..36acc3c
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsBiometricsTestCases"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 5dcadee..9ac12ed 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -52,11 +52,10 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
-import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.EventLog;
import android.util.Pair;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.Surface;
import com.android.internal.R;
@@ -92,55 +91,6 @@
private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
private final LockPatternUtils mLockPatternUtils;
@NonNull private List<ServiceProvider> mServiceProviders;
- @NonNull private final ArrayMap<Integer, TestSession> mTestSessions;
-
- private final class TestSession extends ITestSession.Stub {
- private final int mSensorId;
-
- TestSession(int sensorId) {
- mSensorId = sensorId;
- }
-
- @Override
- public void enableTestHal(boolean enableTestHal) {
- Utils.checkPermission(getContext(), TEST_BIOMETRIC);
- }
-
- @Override
- public void startEnroll(int userId) {
- Utils.checkPermission(getContext(), TEST_BIOMETRIC);
- }
-
- @Override
- public void finishEnroll(int userId) {
- Utils.checkPermission(getContext(), TEST_BIOMETRIC);
- }
-
- @Override
- public void acceptAuthentication(int userId) {
- Utils.checkPermission(getContext(), TEST_BIOMETRIC);
- }
-
- @Override
- public void rejectAuthentication(int userId) {
- Utils.checkPermission(getContext(), TEST_BIOMETRIC);
- }
-
- @Override
- public void notifyAcquired(int userId, int acquireInfo) {
- Utils.checkPermission(getContext(), TEST_BIOMETRIC);
- }
-
- @Override
- public void notifyError(int userId, int errorCode) {
- Utils.checkPermission(getContext(), TEST_BIOMETRIC);
- }
-
- @Override
- public void cleanupInternalState(int userId) {
- Utils.checkPermission(getContext(), TEST_BIOMETRIC);
- }
- }
/**
* Receives the incoming binder calls from FingerprintManager.
@@ -150,20 +100,22 @@
public ITestSession createTestSession(int sensorId, String opPackageName) {
Utils.checkPermission(getContext(), TEST_BIOMETRIC);
- final TestSession session;
- synchronized (mTestSessions) {
- if (!mTestSessions.containsKey(sensorId)) {
- mTestSessions.put(sensorId, new TestSession(sensorId));
+ for (ServiceProvider provider : mServiceProviders) {
+ if (provider.containsSensor(sensorId)) {
+ return provider.createTestSession(sensorId, opPackageName);
}
- session = mTestSessions.get(sensorId);
}
- return session;
+
+ return null;
}
@Override // Binder call
public List<FingerprintSensorPropertiesInternal> getSensorPropertiesInternal(
String opPackageName) {
- Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ if (getContext().checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
+ != PackageManager.PERMISSION_GRANTED) {
+ Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+ }
final List<FingerprintSensorPropertiesInternal> properties =
FingerprintService.this.getSensorProperties();
@@ -424,12 +376,28 @@
final long ident = Binder.clearCallingIdentity();
try {
- for (ServiceProvider provider : mServiceProviders) {
- for (FingerprintSensorPropertiesInternal props :
- provider.getSensorProperties()) {
- if (args.length > 0 && "--proto".equals(args[0])) {
- provider.dumpProto(props.sensorId, fd);
- } else {
+ if (args.length > 1 && "--proto".equals(args[0]) && "--state".equals(args[1])) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ for (ServiceProvider provider : mServiceProviders) {
+ for (FingerprintSensorPropertiesInternal props
+ : provider.getSensorProperties()) {
+ provider.dumpProtoState(props.sensorId, proto);
+ }
+ }
+ proto.flush();
+ } else if (args.length > 0 && "--proto".equals(args[0])) {
+ for (ServiceProvider provider : mServiceProviders) {
+ for (FingerprintSensorPropertiesInternal props
+ : provider.getSensorProperties()) {
+ provider.dumpProtoMetrics(props.sensorId, fd);
+ }
+ }
+ } else {
+ for (ServiceProvider provider : mServiceProviders) {
+ for (FingerprintSensorPropertiesInternal props
+ : provider.getSensorProperties()) {
+ pw.println("Dumping for sensorId: " + props.sensorId
+ + ", provider: " + provider.getClass().getSimpleName());
provider.dumpInternal(props.sensorId, pw);
}
}
@@ -622,7 +590,6 @@
mLockoutResetDispatcher = new LockoutResetDispatcher(context);
mLockPatternUtils = new LockPatternUtils(context);
mServiceProviders = new ArrayList<>();
- mTestSessions = new ArrayMap<>();
initializeAidlHals();
}
@@ -648,7 +615,7 @@
try {
final SensorProps[] props = fp.getSensorProps();
final FingerprintProvider provider =
- new FingerprintProvider(getContext(), props, fqName,
+ new FingerprintProvider(getContext(), props, instance,
mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
mServiceProviders.add(provider);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index c2315fd..1ed66a2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -19,11 +19,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.biometrics.ITestSession;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
+import android.util.proto.ProtoOutputStream;
import android.view.Surface;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
@@ -110,7 +112,11 @@
void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller);
- void dumpProto(int sensorId, @NonNull FileDescriptor fd);
+ void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto);
+
+ void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd);
void dumpInternal(int sensorId, @NonNull PrintWriter pw);
+
+ @NonNull ITestSession createTestSession(int sensorId, @NonNull String opPackageName);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
new file mode 100644
index 0000000..6bb40e6
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -0,0 +1,186 @@
+/*
+ * 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.biometrics.sensors.fingerprint.aidl;
+
+import static android.Manifest.permission.TEST_BIOMETRIC;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.ITestSession;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.Binder;
+import android.util.Slog;
+
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+/**
+ * A test session implementation for {@link FingerprintProvider}. See
+ * {@link android.hardware.biometrics.BiometricTestSession}.
+ */
+class BiometricTestSessionImpl extends ITestSession.Stub {
+
+ private static final String TAG = "BiometricTestSessionImpl";
+
+ @NonNull private final Context mContext;
+ private final int mSensorId;
+ @NonNull private final FingerprintProvider mProvider;
+ @NonNull private final Sensor mSensor;
+ @NonNull private final Set<Integer> mEnrollmentIds;
+ @NonNull private final Random mRandom;
+
+ /**
+ * Internal receiver currently only used for enroll. Results do not need to be forwarded to the
+ * test, since enrollment is a platform-only API. The authentication path is tested through
+ * the public FingerprintManager APIs and does not use this receiver.
+ */
+ private final IFingerprintServiceReceiver mReceiver = new IFingerprintServiceReceiver.Stub() {
+ @Override
+ public void onEnrollResult(Fingerprint fp, int remaining) {
+
+ }
+
+ @Override
+ public void onAcquired(int acquiredInfo, int vendorCode) {
+
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(Fingerprint fp, int userId,
+ boolean isStrongBiometric) {
+
+ }
+
+ @Override
+ public void onFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric) {
+
+ }
+
+ @Override
+ public void onAuthenticationFailed() {
+
+ }
+
+ @Override
+ public void onError(int error, int vendorCode) {
+
+ }
+
+ @Override
+ public void onRemoved(Fingerprint fp, int remaining) {
+
+ }
+
+ @Override
+ public void onChallengeGenerated(int sensorId, long challenge) {
+
+ }
+ };
+
+ BiometricTestSessionImpl(@NonNull Context context, int sensorId,
+ @NonNull FingerprintProvider provider, @NonNull Sensor sensor) {
+ mContext = context;
+ mSensorId = sensorId;
+ mProvider = provider;
+ mSensor = sensor;
+ mEnrollmentIds = new HashSet<>();
+ mRandom = new Random();
+ }
+
+ @Override
+ public void setTestHalEnabled(boolean enabled) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mProvider.setTestHalEnabled(enabled);
+ mSensor.setTestHalEnabled(enabled);
+ }
+
+ @Override
+ public void startEnroll(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
+ mContext.getOpPackageName(), null /* surface */);
+ }
+
+ @Override
+ public void finishEnroll(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ int nextRandomId = mRandom.nextInt();
+ while (mEnrollmentIds.contains(nextRandomId)) {
+ nextRandomId = mRandom.nextInt();
+ }
+
+ mEnrollmentIds.add(nextRandomId);
+ mSensor.getSessionForUser(userId).mHalSessionCallback
+ .onEnrollmentProgress(nextRandomId, 0 /* remaining */);
+ }
+
+ @Override
+ public void acceptAuthentication(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ // Fake authentication with any of the existing fingers
+ List<Fingerprint> fingerprints = FingerprintUtils.getInstance()
+ .getBiometricsForUser(mContext, userId);
+ if (fingerprints.isEmpty()) {
+ Slog.w(TAG, "No fingerprints, returning");
+ return;
+ }
+ final int fid = fingerprints.get(0).getBiometricId();
+ mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationSucceeded(fid,
+ HardwareAuthTokenUtils.toHardwareAuthToken(new byte[69]));
+ }
+
+ @Override
+ public void rejectAuthentication(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mSensor.getSessionForUser(userId).mHalSessionCallback.onAuthenticationFailed();
+ }
+
+ @Override
+ public void notifyAcquired(int userId, int acquireInfo) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mSensor.getSessionForUser(userId).mHalSessionCallback
+ .onAcquired((byte) acquireInfo, 0 /* vendorCode */);
+ }
+
+ @Override
+ public void notifyError(int userId, int errorCode) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mSensor.getSessionForUser(userId).mHalSessionCallback.onError((byte) errorCode,
+ 0 /* vendorCode */);
+ }
+
+ @Override
+ public void cleanupInternalState(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mProvider.scheduleInternalCleanup(mSensorId, userId);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
index fec3cff..2ad1fa3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
@@ -47,6 +47,11 @@
// Nothing to do here
}
+ public void start(@NonNull Callback callback) {
+ super.start(callback);
+ startHalOperation();
+ }
+
@Override
protected void startHalOperation() {
try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index d713f98..4d07f58 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -24,6 +24,7 @@
import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.fingerprint.Fingerprint;
@@ -38,6 +39,7 @@
import android.os.UserManager;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import android.view.Surface;
import com.android.server.biometrics.Utils;
@@ -62,6 +64,8 @@
@SuppressWarnings("deprecation")
public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvider {
+ private boolean mTestHalEnabled;
+
@NonNull private final Context mContext;
@NonNull private final String mHalInstanceName;
@NonNull private final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
@@ -132,7 +136,7 @@
prop.commonProps.maxEnrollmentsPerUser,
prop.sensorType,
true /* resetLockoutRequiresHardwareAuthToken */);
- final Sensor sensor = new Sensor(getTag() + "/" + sensorId, mContext, mHandler,
+ final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
internalProp, gestureAvailabilityDispatcher);
mSensors.put(sensorId, sensor);
@@ -146,6 +150,13 @@
@Nullable
private synchronized IFingerprint getHalInstance() {
+ if (mTestHalEnabled) {
+ // Enabling the test HAL for a single sensor in a multi-sensor HAL currently enables
+ // the test HAL for all sensors under that HAL. This can be updated in the future if
+ // necessary.
+ return new TestHal();
+ }
+
if (mDaemon != null) {
return mDaemon;
}
@@ -153,7 +164,8 @@
Slog.d(getTag(), "Daemon was null, reconnecting");
mDaemon = IFingerprint.Stub.asInterface(
- ServiceManager.waitForDeclaredService(mHalInstanceName));
+ ServiceManager.waitForDeclaredService(IFingerprint.DESCRIPTOR
+ + "/" + mHalInstanceName));
if (mDaemon == null) {
Slog.e(getTag(), "Unable to get daemon");
return null;
@@ -561,7 +573,14 @@
}
@Override
- public void dumpProto(int sensorId, @NonNull FileDescriptor fd) {
+ public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
+ if (mSensors.contains(sensorId)) {
+ mSensors.get(sensorId).dumpProtoState(sensorId, proto);
+ }
+ }
+
+ @Override
+ public void dumpProtoMetrics(int sensorId, @NonNull FileDescriptor fd) {
}
@@ -570,6 +589,12 @@
}
+ @NonNull
+ @Override
+ public ITestSession createTestSession(int sensorId, @NonNull String opPackageName) {
+ return mSensors.get(sensorId).createTestSession();
+ }
+
@Override
public void binderDied() {
Slog.e(getTag(), "HAL died");
@@ -582,4 +607,8 @@
}
});
}
+
+ void setTestHalEnabled(boolean enabled) {
+ mTestHalEnabled = enabled;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index d4ce896..51c30b6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -19,7 +19,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.fingerprint.Error;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.ISession;
@@ -31,11 +33,16 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserManager;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.fingerprint.FingerprintServiceStateProto;
+import com.android.server.biometrics.fingerprint.SensorStateProto;
+import com.android.server.biometrics.fingerprint.UserStateProto;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -48,9 +55,7 @@
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
/**
@@ -59,7 +64,11 @@
*/
@SuppressWarnings("deprecation")
class Sensor implements IBinder.DeathRecipient {
+
+ private boolean mTestHalEnabled;
+
@NonNull private final String mTag;
+ @NonNull private final FingerprintProvider mProvider;
@NonNull private final Context mContext;
@NonNull private final Handler mHandler;
@NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
@@ -91,33 +100,337 @@
});
}
- private static class Session {
+ static class Session {
@NonNull private final String mTag;
@NonNull private final ISession mSession;
private final int mUserId;
- private final ISessionCallback mSessionCallback;
+ @NonNull final HalSessionCallback mHalSessionCallback;
Session(@NonNull String tag, @NonNull ISession session, int userId,
- @NonNull ISessionCallback sessionCallback) {
+ @NonNull HalSessionCallback halSessionCallback) {
mTag = tag;
mSession = session;
mUserId = userId;
- mSessionCallback = sessionCallback;
+ mHalSessionCallback = halSessionCallback;
Slog.d(mTag, "New session created for user: " + userId);
}
}
- Sensor(@NonNull String tag, @NonNull Context context, @NonNull Handler handler,
- @NonNull FingerprintSensorPropertiesInternal sensorProperties,
+ static class HalSessionCallback extends ISessionCallback.Stub {
+
+ /**
+ * Interface to sends results to the HalSessionCallback's owner.
+ */
+ public interface Callback {
+ /**
+ * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
+ */
+ void onHardwareUnavailable();
+ }
+
+ @NonNull private final Context mContext;
+ @NonNull private final Handler mHandler;
+ @NonNull private final String mTag;
+ @NonNull private final BiometricScheduler mScheduler;
+ private final int mSensorId;
+ private final int mUserId;
+ @NonNull private final Callback mCallback;
+
+ HalSessionCallback(@NonNull Context context, @NonNull Handler handler, @NonNull String tag,
+ @NonNull BiometricScheduler scheduler, int sensorId, int userId,
+ @NonNull Callback callback) {
+ mContext = context;
+ mHandler = handler;
+ mTag = tag;
+ mScheduler = scheduler;
+ mSensorId = sensorId;
+ mUserId = userId;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onStateChanged(int cookie, byte state) {
+ // TODO(b/162973174)
+ }
+
+ @Override
+ public void onChallengeGenerated(long challenge) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof FingerprintGenerateChallengeClient)) {
+ Slog.e(mTag, "onChallengeGenerated for wrong client: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final FingerprintGenerateChallengeClient generateChallengeClient =
+ (FingerprintGenerateChallengeClient) client;
+ generateChallengeClient.onChallengeGenerated(mSensorId, mUserId, challenge);
+ });
+ }
+
+ @Override
+ public void onChallengeRevoked(long challenge) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof FingerprintRevokeChallengeClient)) {
+ Slog.e(mTag, "onChallengeRevoked for wrong client: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final FingerprintRevokeChallengeClient revokeChallengeClient =
+ (FingerprintRevokeChallengeClient) client;
+ revokeChallengeClient.onChallengeRevoked(mSensorId, mUserId, challenge);
+ });
+ }
+
+ @Override
+ public void onAcquired(byte info, int vendorCode) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof AcquisitionClient)) {
+ Slog.e(mTag, "onAcquired for non-acquisition client: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
+ acquisitionClient.onAcquired(info, vendorCode);
+ });
+ }
+
+ @Override
+ public void onError(byte error, int vendorCode) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ Slog.d(mTag, "onError"
+ + ", client: " + Utils.getClientName(client)
+ + ", error: " + error
+ + ", vendorCode: " + vendorCode);
+ if (!(client instanceof Interruptable)) {
+ Slog.e(mTag, "onError for non-error consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final Interruptable interruptable = (Interruptable) client;
+ interruptable.onError(error, vendorCode);
+
+ if (error == Error.HW_UNAVAILABLE) {
+ mCallback.onHardwareUnavailable();
+ }
+ });
+ }
+
+ @Override
+ public void onEnrollmentProgress(int enrollmentId, int remaining) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof FingerprintEnrollClient)) {
+ Slog.e(mTag, "onEnrollmentProgress for non-enroll client: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final int currentUserId = client.getTargetUserId();
+ final CharSequence name = FingerprintUtils.getInstance(mSensorId)
+ .getUniqueName(mContext, currentUserId);
+ final Fingerprint fingerprint = new Fingerprint(name, enrollmentId, mSensorId);
+
+ final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client;
+ enrollClient.onEnrollResult(fingerprint, remaining);
+ });
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(int enrollmentId, HardwareAuthToken hat) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof AuthenticationConsumer)) {
+ Slog.e(mTag, "onAuthenticationSucceeded for non-authentication consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final AuthenticationConsumer authenticationConsumer =
+ (AuthenticationConsumer) client;
+ final Fingerprint fp = new Fingerprint("", enrollmentId, mSensorId);
+ final byte[] byteArray = HardwareAuthTokenUtils.toByteArray(hat);
+ final ArrayList<Byte> byteList = new ArrayList<>();
+ for (byte b : byteArray) {
+ byteList.add(b);
+ }
+
+ authenticationConsumer.onAuthenticated(fp, true /* authenticated */, byteList);
+ });
+ }
+
+ @Override
+ public void onAuthenticationFailed() {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof AuthenticationConsumer)) {
+ Slog.e(mTag, "onAuthenticationFailed for non-authentication consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final AuthenticationConsumer authenticationConsumer =
+ (AuthenticationConsumer) client;
+ final Fingerprint fp = new Fingerprint("", 0 /* enrollmentId */, mSensorId);
+ authenticationConsumer
+ .onAuthenticated(fp, false /* authenticated */, null /* hat */);
+ });
+ }
+
+ @Override
+ public void onLockoutTimed(long durationMillis) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof LockoutConsumer)) {
+ Slog.e(mTag, "onLockoutTimed for non-lockout consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final LockoutConsumer lockoutConsumer = (LockoutConsumer) client;
+ lockoutConsumer.onLockoutTimed(durationMillis);
+ });
+ }
+
+ @Override
+ public void onLockoutPermanent() {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof LockoutConsumer)) {
+ Slog.e(mTag, "onLockoutPermanent for non-lockout consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final LockoutConsumer lockoutConsumer = (LockoutConsumer) client;
+ lockoutConsumer.onLockoutPermanent();
+ });
+ }
+
+ @Override
+ public void onLockoutCleared() {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof FingerprintResetLockoutClient)) {
+ Slog.e(mTag, "onLockoutCleared for non-resetLockout client: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final FingerprintResetLockoutClient resetLockoutClient =
+ (FingerprintResetLockoutClient) client;
+ resetLockoutClient.onLockoutCleared();
+ });
+ }
+
+ @Override
+ public void onInteractionDetected() {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof FingerprintDetectClient)) {
+ Slog.e(mTag, "onInteractionDetected for non-detect client: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final FingerprintDetectClient fingerprintDetectClient =
+ (FingerprintDetectClient) client;
+ fingerprintDetectClient.onInteractionDetected();
+ });
+ }
+
+ @Override
+ public void onEnrollmentsEnumerated(int[] enrollmentIds) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof EnumerateConsumer)) {
+ Slog.e(mTag, "onEnrollmentsEnumerated for non-enumerate consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final EnumerateConsumer enumerateConsumer =
+ (EnumerateConsumer) client;
+ if (enrollmentIds.length > 0) {
+ for (int i = 0; i < enrollmentIds.length; i++) {
+ final Fingerprint fp = new Fingerprint("", enrollmentIds[i], mSensorId);
+ enumerateConsumer.onEnumerationResult(fp, enrollmentIds.length - i - 1);
+ }
+ } else {
+ enumerateConsumer.onEnumerationResult(null /* identifier */, 0);
+ }
+ });
+ }
+
+ @Override
+ public void onEnrollmentsRemoved(int[] enrollmentIds) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof RemovalConsumer)) {
+ Slog.e(mTag, "onRemoved for non-removal consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final RemovalConsumer removalConsumer = (RemovalConsumer) client;
+ if (enrollmentIds.length > 0) {
+ for (int i = 0; i < enrollmentIds.length; i++) {
+ final Fingerprint fp = new Fingerprint("", enrollmentIds[i], mSensorId);
+ removalConsumer.onRemoved(fp, enrollmentIds.length - i - 1);
+ }
+ } else {
+ removalConsumer.onRemoved(null, 0);
+ }
+ });
+ }
+
+ @Override
+ public void onAuthenticatorIdRetrieved(long authenticatorId) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof FingerprintGetAuthenticatorIdClient)) {
+ Slog.e(mTag, "onAuthenticatorIdRetrieved for wrong consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final FingerprintGetAuthenticatorIdClient getAuthenticatorIdClient =
+ (FingerprintGetAuthenticatorIdClient) client;
+ getAuthenticatorIdClient.onAuthenticatorIdRetrieved(authenticatorId);
+ });
+ }
+
+ @Override
+ public void onAuthenticatorIdInvalidated() {
+ // TODO(159667191)
+ }
+ }
+
+ Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
+ @NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
mTag = tag;
+ mProvider = provider;
mContext = context;
mHandler = handler;
mSensorProperties = sensorProperties;
mScheduler = new BiometricScheduler(tag, gestureAvailabilityDispatcher);
mLockoutCache = new LockoutCache();
mAuthenticatorIds = new HashMap<>();
- mLazySession = () -> mCurrentSession != null ? mCurrentSession.mSession : null;
+ mLazySession = () -> {
+ if (mTestHalEnabled) {
+ return new TestSession(mCurrentSession.mHalSessionCallback);
+ } else {
+ return mCurrentSession != null ? mCurrentSession.mSession : null;
+ }
+ };
}
@NonNull ClientMonitor.LazyDaemon<ISession> getLazySession() {
@@ -133,278 +446,31 @@
return mCurrentSession != null && mCurrentSession.mUserId == userId;
}
+ @Nullable Session getSessionForUser(int userId) {
+ if (mCurrentSession != null && mCurrentSession.mUserId == userId) {
+ return mCurrentSession;
+ } else {
+ return null;
+ }
+ }
+
+ @NonNull ITestSession createTestSession() {
+ return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, mProvider, this);
+ }
+
void createNewSession(@NonNull IFingerprint daemon, int sensorId, int userId)
throws RemoteException {
- final ISessionCallback callback = new ISessionCallback.Stub() {
- @Override
- public void onStateChanged(int cookie, byte state) {
- // TODO(b/162973174)
- }
- @Override
- public void onChallengeGenerated(long challenge) {
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintGenerateChallengeClient)) {
- Slog.e(mTag, "onChallengeGenerated for wrong client: "
- + Utils.getClientName(client));
- return;
- }
-
- final FingerprintGenerateChallengeClient generateChallengeClient =
- (FingerprintGenerateChallengeClient) client;
- generateChallengeClient.onChallengeGenerated(sensorId, userId, challenge);
- });
- }
-
- @Override
- public void onChallengeRevoked(long challenge) {
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintRevokeChallengeClient)) {
- Slog.e(mTag, "onChallengeRevoked for wrong client: "
- + Utils.getClientName(client));
- return;
- }
-
- final FingerprintRevokeChallengeClient revokeChallengeClient =
- (FingerprintRevokeChallengeClient) client;
- revokeChallengeClient.onChallengeRevoked(sensorId, userId, challenge);
- });
- }
-
- @Override
- public void onAcquired(byte info, int vendorCode) {
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (!(client instanceof AcquisitionClient)) {
- Slog.e(mTag, "onAcquired for non-acquisition client: "
- + Utils.getClientName(client));
- return;
- }
-
- final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
- acquisitionClient.onAcquired(info, vendorCode);
- });
- }
-
- @Override
- public void onError(byte error, int vendorCode) {
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- Slog.d(mTag, "onError"
- + ", client: " + Utils.getClientName(client)
- + ", error: " + error
- + ", vendorCode: " + vendorCode);
- if (!(client instanceof Interruptable)) {
- Slog.e(mTag, "onError for non-error consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final Interruptable interruptable = (Interruptable) client;
- interruptable.onError(error, vendorCode);
-
- if (error == Error.HW_UNAVAILABLE) {
- Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
- mCurrentSession = null;
- }
- });
- }
-
- @Override
- public void onEnrollmentProgress(int enrollmentId, int remaining) {
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintEnrollClient)) {
- Slog.e(mTag, "onEnrollmentProgress for non-enroll client: "
- + Utils.getClientName(client));
- return;
- }
-
- final int currentUserId = client.getTargetUserId();
- final CharSequence name = FingerprintUtils.getInstance(sensorId)
- .getUniqueName(mContext, currentUserId);
- final Fingerprint fingerprint = new Fingerprint(name, enrollmentId, sensorId);
-
- final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client;
- enrollClient.onEnrollResult(fingerprint, remaining);
- });
- }
-
- @Override
- public void onAuthenticationSucceeded(int enrollmentId, HardwareAuthToken hat) {
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (!(client instanceof AuthenticationConsumer)) {
- Slog.e(mTag, "onAuthenticationSucceeded for non-authentication consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final AuthenticationConsumer authenticationConsumer =
- (AuthenticationConsumer) client;
- final Fingerprint fp = new Fingerprint("", enrollmentId, sensorId);
- final byte[] byteArray = HardwareAuthTokenUtils.toByteArray(hat);
- final ArrayList<Byte> byteList = new ArrayList<>();
- for (byte b : byteArray) {
- byteList.add(b);
- }
-
- authenticationConsumer.onAuthenticated(fp, true /* authenticated */, byteList);
- });
- }
-
- @Override
- public void onAuthenticationFailed() {
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (!(client instanceof AuthenticationConsumer)) {
- Slog.e(mTag, "onAuthenticationFailed for non-authentication consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final AuthenticationConsumer authenticationConsumer =
- (AuthenticationConsumer) client;
- final Fingerprint fp = new Fingerprint("", 0 /* enrollmentId */, sensorId);
- authenticationConsumer
- .onAuthenticated(fp, false /* authenticated */, null /* hat */);
- });
- }
-
- @Override
- public void onLockoutTimed(long durationMillis) {
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (!(client instanceof LockoutConsumer)) {
- Slog.e(mTag, "onLockoutTimed for non-lockout consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final LockoutConsumer lockoutConsumer = (LockoutConsumer) client;
- lockoutConsumer.onLockoutTimed(durationMillis);
- });
- }
-
- @Override
- public void onLockoutPermanent() {
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (!(client instanceof LockoutConsumer)) {
- Slog.e(mTag, "onLockoutPermanent for non-lockout consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final LockoutConsumer lockoutConsumer = (LockoutConsumer) client;
- lockoutConsumer.onLockoutPermanent();
- });
- }
-
- @Override
- public void onLockoutCleared() {
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintResetLockoutClient)) {
- Slog.e(mTag, "onLockoutCleared for non-resetLockout client: "
- + Utils.getClientName(client));
- return;
- }
-
- final FingerprintResetLockoutClient resetLockoutClient =
- (FingerprintResetLockoutClient) client;
- resetLockoutClient.onLockoutCleared();
- });
- }
-
- @Override
- public void onInteractionDetected() {
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintDetectClient)) {
- Slog.e(mTag, "onInteractionDetected for non-detect client: "
- + Utils.getClientName(client));
- return;
- }
-
- final FingerprintDetectClient fingerprintDetectClient =
- (FingerprintDetectClient) client;
- fingerprintDetectClient.onInteractionDetected();
- });
- }
-
- @Override
- public void onEnrollmentsEnumerated(int[] enrollmentIds) {
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (!(client instanceof EnumerateConsumer)) {
- Slog.e(mTag, "onEnrollmentsEnumerated for non-enumerate consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final EnumerateConsumer enumerateConsumer =
- (EnumerateConsumer) client;
- if (enrollmentIds.length > 0) {
- for (int i = 0; i < enrollmentIds.length; i++) {
- final Fingerprint fp = new Fingerprint("", enrollmentIds[i], sensorId);
- enumerateConsumer.onEnumerationResult(fp, enrollmentIds.length - i - 1);
- }
- } else {
- enumerateConsumer.onEnumerationResult(null /* identifier */, 0);
- }
- });
- }
-
- @Override
- public void onEnrollmentsRemoved(int[] enrollmentIds) {
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (!(client instanceof RemovalConsumer)) {
- Slog.e(mTag, "onRemoved for non-removal consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final RemovalConsumer removalConsumer = (RemovalConsumer) client;
- if (enrollmentIds.length > 0) {
- for (int i = 0; i < enrollmentIds.length; i++) {
- final Fingerprint fp = new Fingerprint("", enrollmentIds[i], sensorId);
- removalConsumer.onRemoved(fp, enrollmentIds.length - i - 1);
- }
- } else {
- removalConsumer.onRemoved(null, 0);
- }
- });
- }
-
- @Override
- public void onAuthenticatorIdRetrieved(long authenticatorId) {
- mHandler.post(() -> {
- final ClientMonitor<?> client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintGetAuthenticatorIdClient)) {
- Slog.e(mTag, "onAuthenticatorIdRetrieved for wrong consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final FingerprintGetAuthenticatorIdClient getAuthenticatorIdClient =
- (FingerprintGetAuthenticatorIdClient) client;
- getAuthenticatorIdClient.onAuthenticatorIdRetrieved(authenticatorId);
- });
- }
-
- @Override
- public void onAuthenticatorIdInvalidated() {
- // TODO(159667191)
- }
+ final HalSessionCallback.Callback callback = () -> {
+ Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
+ mCurrentSession = null;
};
+ final HalSessionCallback resultController = new HalSessionCallback(mContext, mHandler,
+ mTag, mScheduler, sensorId, userId, callback);
- final ISession newSession = daemon.createSession(sensorId, userId, callback);
+ final ISession newSession = daemon.createSession(sensorId, userId, resultController);
newSession.asBinder().linkToDeath(this, 0 /* flags */);
- mCurrentSession = new Session(mTag, newSession, userId, callback);
+ mCurrentSession = new Session(mTag, newSession, userId, resultController);
}
@NonNull BiometricScheduler getScheduler() {
@@ -418,4 +484,27 @@
@NonNull Map<Integer, Long> getAuthenticatorIds() {
return mAuthenticatorIds;
}
+
+ void setTestHalEnabled(boolean enabled) {
+ mTestHalEnabled = enabled;
+ }
+
+ void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
+ final long sensorToken = proto.start(FingerprintServiceStateProto.SENSOR_STATES);
+
+ proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
+ proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null);
+
+ for (UserInfo user : UserManager.get(mContext).getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+
+ final long userToken = proto.start(SensorStateProto.USER_STATES);
+ proto.write(UserStateProto.USER_ID, userId);
+ proto.write(UserStateProto.NUM_ENROLLED, FingerprintUtils.getInstance()
+ .getBiometricsForUser(mContext, userId).size());
+ proto.end(userToken);
+ }
+
+ proto.end(sensorToken);
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
new file mode 100644
index 0000000..8c9a269
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java
@@ -0,0 +1,111 @@
+/*
+ * 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.biometrics.sensors.fingerprint.aidl;
+
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.biometrics.fingerprint.ISessionCallback;
+import android.hardware.biometrics.fingerprint.SensorProps;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.os.Binder;
+import android.os.IBinder;
+
+/**
+ * Test HAL that provides only provides no-ops.
+ */
+public class TestHal extends IFingerprint.Stub {
+ @Override
+ public SensorProps[] getSensorProps() {
+ return new SensorProps[0];
+ }
+
+ @Override
+ public ISession createSession(int sensorId, int userId, ISessionCallback cb) {
+ return new ISession() {
+ @Override
+ public void generateChallenge(int cookie, int timeoutSec) {
+
+ }
+
+ @Override
+ public void revokeChallenge(int cookie, long challenge) {
+
+ }
+
+ @Override
+ public ICancellationSignal enroll(int cookie, HardwareAuthToken hat) {
+ return null;
+ }
+
+ @Override
+ public ICancellationSignal authenticate(int cookie, long operationId) {
+ return null;
+ }
+
+ @Override
+ public ICancellationSignal detectInteraction(int cookie) {
+ return null;
+ }
+
+ @Override
+ public void enumerateEnrollments(int cookie) {
+
+ }
+
+ @Override
+ public void removeEnrollments(int cookie, int[] enrollmentIds) {
+
+ }
+
+ @Override
+ public void getAuthenticatorId(int cookie) {
+
+ }
+
+ @Override
+ public void invalidateAuthenticatorId(int cookie, HardwareAuthToken hat) {
+
+ }
+
+ @Override
+ public void resetLockout(int cookie, HardwareAuthToken hat) {
+
+ }
+
+ @Override
+ public void onPointerDown(int pointerId, int x, int y, float minor, float major) {
+
+ }
+
+ @Override
+ public void onPointerUp(int pointerId) {
+
+ }
+
+ @Override
+ public void onUiReady() {
+
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return new Binder();
+ }
+ };
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java
new file mode 100644
index 0000000..d5afd0c
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java
@@ -0,0 +1,106 @@
+/*
+ * 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.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.util.Slog;
+
+/**
+ * Test HAL that provides only provides mostly no-ops.
+ */
+class TestSession extends ISession.Stub {
+
+ private static final String TAG = "TestSession";
+
+ @NonNull private final Sensor.HalSessionCallback mHalSessionCallback;
+
+ TestSession(@NonNull Sensor.HalSessionCallback halSessionCallback) {
+ mHalSessionCallback = halSessionCallback;
+ }
+
+ @Override
+ public void generateChallenge(int cookie, int timeoutSec) {
+
+ }
+
+ @Override
+ public void revokeChallenge(int cookie, long challenge) {
+
+ }
+
+ @Override
+ public ICancellationSignal enroll(int cookie, HardwareAuthToken hat) {
+ Slog.d(TAG, "enroll");
+ return null;
+ }
+
+ @Override
+ public ICancellationSignal authenticate(int cookie, long operationId) {
+ Slog.d(TAG, "authenticate");
+ return null;
+ }
+
+ @Override
+ public ICancellationSignal detectInteraction(int cookie) {
+ return null;
+ }
+
+ @Override
+ public void enumerateEnrollments(int cookie) {
+ Slog.d(TAG, "enumerate");
+ }
+
+ @Override
+ public void removeEnrollments(int cookie, int[] enrollmentIds) {
+ Slog.d(TAG, "remove");
+ }
+
+ @Override
+ public void getAuthenticatorId(int cookie) {
+ Slog.d(TAG, "getAuthenticatorId");
+ // Immediately return a value so the framework can continue with subsequent requests.
+ mHalSessionCallback.onAuthenticatorIdRetrieved(0);
+ }
+
+ @Override
+ public void invalidateAuthenticatorId(int cookie, HardwareAuthToken hat) {
+
+ }
+
+ @Override
+ public void resetLockout(int cookie, HardwareAuthToken hat) {
+
+ }
+
+ @Override
+ public void onPointerDown(int pointerId, int x, int y, float minor, float major) {
+
+ }
+
+ @Override
+ public void onPointerUp(int pointerId) {
+
+ }
+
+ @Override
+ public void onUiReady() {
+
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
new file mode 100644
index 0000000..e0ea990
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
@@ -0,0 +1,185 @@
+/*
+ * 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.biometrics.sensors.fingerprint.hidl;
+
+import static android.Manifest.permission.TEST_BIOMETRIC;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.ITestSession;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.Binder;
+import android.util.Slog;
+
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+/**
+ * A test session implementation for the {@link Fingerprint21} provider. See
+ * {@link android.hardware.biometrics.BiometricTestSession}.
+ */
+public class BiometricTestSessionImpl extends ITestSession.Stub {
+
+ private static final String TAG = "BiometricTestSessionImpl";
+
+ @NonNull private final Context mContext;
+ private final int mSensorId;
+ @NonNull private final Fingerprint21 mFingerprint21;
+ @NonNull private final Fingerprint21.HalResultController mHalResultController;
+ @NonNull private final Set<Integer> mEnrollmentIds;
+ @NonNull private final Random mRandom;
+
+ /**
+ * Internal receiver currently only used for enroll. Results do not need to be forwarded to the
+ * test, since enrollment is a platform-only API. The authentication path is tested through
+ * the public FingerprintManager APIs and does not use this receiver.
+ */
+ private final IFingerprintServiceReceiver mReceiver = new IFingerprintServiceReceiver.Stub() {
+ @Override
+ public void onEnrollResult(Fingerprint fp, int remaining) {
+
+ }
+
+ @Override
+ public void onAcquired(int acquiredInfo, int vendorCode) {
+
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(Fingerprint fp, int userId,
+ boolean isStrongBiometric) {
+
+ }
+
+ @Override
+ public void onFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric) {
+
+ }
+
+ @Override
+ public void onAuthenticationFailed() {
+
+ }
+
+ @Override
+ public void onError(int error, int vendorCode) {
+
+ }
+
+ @Override
+ public void onRemoved(Fingerprint fp, int remaining) {
+
+ }
+
+ @Override
+ public void onChallengeGenerated(int sensorId, long challenge) {
+
+ }
+ };
+
+ BiometricTestSessionImpl(@NonNull Context context, int sensorId,
+ @NonNull Fingerprint21 fingerprint21,
+ @NonNull Fingerprint21.HalResultController halResultController) {
+ mContext = context;
+ mSensorId = sensorId;
+ mFingerprint21 = fingerprint21;
+ mHalResultController = halResultController;
+ mEnrollmentIds = new HashSet<>();
+ mRandom = new Random();
+ }
+
+ @Override
+ public void setTestHalEnabled(boolean enabled) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mFingerprint21.setTestHalEnabled(enabled);
+ }
+
+ @Override
+ public void startEnroll(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mFingerprint21.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
+ mContext.getOpPackageName(), null /* surface */);
+ }
+
+ @Override
+ public void finishEnroll(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ int nextRandomId = mRandom.nextInt();
+ while (mEnrollmentIds.contains(nextRandomId)) {
+ nextRandomId = mRandom.nextInt();
+ }
+
+ mEnrollmentIds.add(nextRandomId);
+ mHalResultController.onEnrollResult(0 /* deviceId */,
+ nextRandomId /* fingerId */, userId, 0);
+ }
+
+ @Override
+ public void acceptAuthentication(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ // Fake authentication with any of the existing fingers
+ List<Fingerprint> fingerprints = FingerprintUtils.getInstance()
+ .getBiometricsForUser(mContext, userId);
+ if (fingerprints.isEmpty()) {
+ Slog.w(TAG, "No fingerprints, returning");
+ return;
+ }
+ final int fid = fingerprints.get(0).getBiometricId();
+ final ArrayList<Byte> hat = new ArrayList<>(Collections.nCopies(69, (byte) 0));
+ mHalResultController.onAuthenticated(0 /* deviceId */, fid, userId, hat);
+ }
+
+ @Override
+ public void rejectAuthentication(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mHalResultController.onAuthenticated(0 /* deviceId */, 0 /* fingerId */, userId, null);
+ }
+
+ @Override
+ public void notifyAcquired(int userId, int acquireInfo) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mHalResultController.onAcquired(0 /* deviceId */, acquireInfo, 0 /* vendorCode */);
+ }
+
+ @Override
+ public void notifyError(int userId, int errorCode) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mHalResultController.onError(0 /* deviceId */, errorCode, 0 /* vendorCode */);
+ }
+
+ @Override
+ public void cleanupInternalState(int userId) {
+ Utils.checkPermission(mContext, TEST_BIOMETRIC);
+
+ mFingerprint21.scheduleInternalCleanup(mSensorId, userId);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index ab4427c..241c911 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -29,6 +29,7 @@
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
import android.hardware.fingerprint.Fingerprint;
@@ -51,8 +52,11 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.fingerprint.FingerprintServiceDumpProto;
+import com.android.server.biometrics.fingerprint.FingerprintServiceStateProto;
import com.android.server.biometrics.fingerprint.FingerprintUserStatsProto;
import com.android.server.biometrics.fingerprint.PerformanceStatsProto;
+import com.android.server.biometrics.fingerprint.SensorStateProto;
+import com.android.server.biometrics.fingerprint.UserStateProto;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
@@ -91,6 +95,8 @@
private static final String TAG = "Fingerprint21";
private static final int ENROLL_TIMEOUT_SEC = 60;
+ private boolean mTestHalEnabled;
+
final Context mContext;
private final IActivityTaskManager mActivityTaskManager;
@NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
@@ -391,6 +397,10 @@
}
private synchronized IBiometricsFingerprint getDaemon() {
+ if (mTestHalEnabled) {
+ return new TestHal();
+ }
+
if (mDaemon != null) {
return mDaemon;
}
@@ -693,7 +703,27 @@
}
@Override
- public void dumpProto(int sensorId, FileDescriptor fd) {
+ public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto) {
+ final long sensorToken = proto.start(FingerprintServiceStateProto.SENSOR_STATES);
+
+ proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
+ proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null);
+
+ for (UserInfo user : UserManager.get(mContext).getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+
+ final long userToken = proto.start(SensorStateProto.USER_STATES);
+ proto.write(UserStateProto.USER_ID, userId);
+ proto.write(UserStateProto.NUM_ENROLLED, FingerprintUtils.getInstance()
+ .getBiometricsForUser(mContext, userId).size());
+ proto.end(userToken);
+ }
+
+ proto.end(sensorToken);
+ }
+
+ @Override
+ public void dumpProtoMetrics(int sensorId, FileDescriptor fd) {
PerformanceTracker tracker =
PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId);
@@ -771,4 +801,15 @@
pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount());
mScheduler.dump(pw);
}
+
+ void setTestHalEnabled(boolean enabled) {
+ mTestHalEnabled = enabled;
+ }
+
+ @NonNull
+ @Override
+ public ITestSession createTestSession(int sensorId, @NonNull String opPackageName) {
+ return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, this,
+ mHalResultController);
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java
new file mode 100644
index 0000000..86c0875
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/TestHal.java
@@ -0,0 +1,91 @@
+/*
+ * 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.biometrics.sensors.fingerprint.hidl;
+
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
+import android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint;
+import android.os.RemoteException;
+
+/**
+ * Test HAL that provides only provides no-ops.
+ */
+public class TestHal extends IBiometricsFingerprint.Stub {
+ @Override
+ public boolean isUdfps(int sensorId) {
+ return false;
+ }
+
+ @Override
+ public void onFingerDown(int x, int y, float minor, float major) {
+
+ }
+
+ @Override
+ public void onFingerUp() {
+
+ }
+
+ @Override
+ public long setNotify(IBiometricsFingerprintClientCallback clientCallback) {
+ return 0;
+ }
+
+ @Override
+ public long preEnroll() {
+ return 0;
+ }
+
+ @Override
+ public int enroll(byte[] hat, int gid, int timeoutSec) {
+ return 0;
+ }
+
+ @Override
+ public int postEnroll() {
+ return 0;
+ }
+
+ @Override
+ public long getAuthenticatorId() {
+ return 0;
+ }
+
+ @Override
+ public int cancel() {
+ return 0;
+ }
+
+ @Override
+ public int enumerate() {
+ return 0;
+ }
+
+ @Override
+ public int remove(int gid, int fid) {
+ return 0;
+ }
+
+ @Override
+ public int setActiveGroup(int gid, String storePath) {
+ return 0;
+ }
+
+ @Override
+ public int authenticate(long operationId, int gid) {
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index a0eafb4..b7e188c 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -392,6 +392,7 @@
} catch (RemoteException ex) {
Slog.e(TAG, "Failed closing announcement listener", ex);
}
+ hwCloseHandle.value = null;
}
};
}
diff --git a/services/core/java/com/android/server/connectivity/DataConnectionStats.java b/services/core/java/com/android/server/connectivity/DataConnectionStats.java
index 0304cdc..fbd089c 100644
--- a/services/core/java/com/android/server/connectivity/DataConnectionStats.java
+++ b/services/core/java/com/android/server/connectivity/DataConnectionStats.java
@@ -19,12 +19,12 @@
import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
-import android.os.Looper;
import android.os.RemoteException;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneStateListener;
@@ -36,6 +36,9 @@
import com.android.internal.app.IBatteryStats;
import com.android.server.am.BatteryStatsService;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
public class DataConnectionStats extends BroadcastReceiver {
private static final String TAG = "DataConnectionStats";
private static final boolean DEBUG = false;
@@ -49,13 +52,13 @@
private SignalStrength mSignalStrength;
private ServiceState mServiceState;
private int mDataState = TelephonyManager.DATA_DISCONNECTED;
- private int mNrState = NetworkRegistrationInfo.NR_STATE_NONE;
public DataConnectionStats(Context context, Handler listenerHandler) {
mContext = context;
mBatteryStats = BatteryStatsService.getService();
mListenerHandler = listenerHandler;
- mPhoneStateListener = new PhoneStateListenerImpl(listenerHandler.getLooper());
+ mPhoneStateListener =
+ new PhoneStateListenerImpl(new PhoneStateListenerExecutor(listenerHandler));
}
public void startMonitoring() {
@@ -96,7 +99,7 @@
: regInfo.getAccessNetworkTechnology();
// If the device is in NSA NR connection the networkType will report as LTE.
// For cell dwell rate metrics, this should report NR instead.
- if (mNrState == NetworkRegistrationInfo.NR_STATE_CONNECTED) {
+ if (regInfo != null && regInfo.getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED) {
networkType = TelephonyManager.NETWORK_TYPE_NR;
}
if (DEBUG) Log.d(TAG, String.format("Noting data connection for network type %s: %svisible",
@@ -140,9 +143,24 @@
&& mServiceState.getState() != ServiceState.STATE_POWER_OFF;
}
+ private static class PhoneStateListenerExecutor implements Executor {
+ @NonNull
+ private final Handler mHandler;
+
+ PhoneStateListenerExecutor(@NonNull Handler handler) {
+ mHandler = handler;
+ }
+ @Override
+ public void execute(Runnable command) {
+ if (!mHandler.post(command)) {
+ throw new RejectedExecutionException(mHandler + " is shutting down");
+ }
+ }
+ }
+
private class PhoneStateListenerImpl extends PhoneStateListener {
- PhoneStateListenerImpl(Looper looper) {
- super(looper);
+ PhoneStateListenerImpl(Executor executor) {
+ super(executor);
}
@Override
@@ -153,7 +171,6 @@
@Override
public void onServiceStateChanged(ServiceState state) {
mServiceState = state;
- mNrState = state.getNrState();
notePhoneDataConnectionState();
}
diff --git a/services/core/java/com/android/server/connectivity/LingerMonitor.java b/services/core/java/com/android/server/connectivity/LingerMonitor.java
index 04c000f..f99f4c6 100644
--- a/services/core/java/com/android/server/connectivity/LingerMonitor.java
+++ b/services/core/java/com/android/server/connectivity/LingerMonitor.java
@@ -159,8 +159,11 @@
@VisibleForTesting
protected PendingIntent createNotificationIntent() {
- return PendingIntent.getActivityAsUser(mContext, 0, CELLULAR_SETTINGS,
- PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
+ return PendingIntent.getActivity(
+ mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
+ 0 /* requestCode */,
+ CELLULAR_SETTINGS,
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
}
// Removes any notification that was put up as a result of switching to nai.
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 34b0aa2..26356b4 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -325,7 +325,8 @@
public void setProvNotificationVisible(boolean visible, int id, String action) {
if (visible) {
Intent intent = new Intent(action);
- PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
showNotification(id, NotificationType.SIGN_IN, null, null, pendingIntent, false);
} else {
clearNotification(id);
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index e218793..4c2b1e3 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1969,8 +1969,9 @@
*/
public PendingIntent pendingIntentGetActivityAsUser(
Intent intent, int flags, UserHandle user) {
- return PendingIntent.getActivityAsUser(mContext, 0 /*request*/, intent, flags,
- null /*options*/, user);
+ return PendingIntent.getActivity(
+ mContext.createContextAsUser(user, 0 /* flags */), 0 /* requestCode */,
+ intent, flags);
}
/**
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index c686ba4..6a4ca8d 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -210,7 +210,7 @@
private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock";
- private static final boolean USE_WTF_FOR_ACCOUNT_ERROR = false;
+ private static final boolean USE_WTF_FOR_ACCOUNT_ERROR = true;
private static final int SYNC_OP_STATE_VALID = 0;
// "1" used to include errors 3, 4 and 5 but now it's split up.
@@ -231,7 +231,7 @@
private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0];
- // TODO: add better locking around mRunningAccounts
+ private final Object mAccountsLock = new Object();
private volatile AccountAndUser[] mRunningAccounts = INITIAL_ACCOUNTS_ARRAY;
volatile private PowerManager.WakeLock mSyncManagerWakeLock;
@@ -933,19 +933,21 @@
}
AccountAndUser[] accounts = null;
- if (requestedAccount != null) {
- if (userId != UserHandle.USER_ALL) {
- accounts = new AccountAndUser[]{new AccountAndUser(requestedAccount, userId)};
- } else {
- for (AccountAndUser runningAccount : mRunningAccounts) {
- if (requestedAccount.equals(runningAccount.account)) {
- accounts = ArrayUtils.appendElement(AccountAndUser.class,
- accounts, runningAccount);
+ synchronized (mAccountsLock) {
+ if (requestedAccount != null) {
+ if (userId != UserHandle.USER_ALL) {
+ accounts = new AccountAndUser[]{new AccountAndUser(requestedAccount, userId)};
+ } else {
+ for (AccountAndUser runningAccount : mRunningAccounts) {
+ if (requestedAccount.equals(runningAccount.account)) {
+ accounts = ArrayUtils.appendElement(AccountAndUser.class,
+ accounts, runningAccount);
+ }
}
}
+ } else {
+ accounts = mRunningAccounts;
}
- } else {
- accounts = mRunningAccounts;
}
if (ArrayUtils.isEmpty(accounts)) {
@@ -3228,40 +3230,43 @@
}
private void updateRunningAccountsH(EndPoint syncTargets) {
- AccountAndUser[] oldAccounts = mRunningAccounts;
- mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Slog.v(TAG, "Accounts list: ");
- for (AccountAndUser acc : mRunningAccounts) {
- Slog.v(TAG, acc.toString());
+ synchronized (mAccountsLock) {
+ AccountAndUser[] oldAccounts = mRunningAccounts;
+ mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.v(TAG, "Accounts list: ");
+ for (AccountAndUser acc : mRunningAccounts) {
+ Slog.v(TAG, acc.toString());
+ }
}
- }
- if (mLogger.enabled()) {
- mLogger.log("updateRunningAccountsH: ", Arrays.toString(mRunningAccounts));
- }
- removeStaleAccounts();
-
- AccountAndUser[] accounts = mRunningAccounts;
- for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
- if (!containsAccountAndUser(accounts,
- currentSyncContext.mSyncOperation.target.account,
- currentSyncContext.mSyncOperation.target.userId)) {
- Log.d(TAG, "canceling sync since the account is no longer running");
- sendSyncFinishedOrCanceledMessage(currentSyncContext,
- null /* no result since this is a cancel */);
+ if (mLogger.enabled()) {
+ mLogger.log("updateRunningAccountsH: ", Arrays.toString(mRunningAccounts));
}
- }
+ removeStaleAccounts();
- if (syncTargets != null) {
- // On account add, check if there are any settings to be restored.
- for (AccountAndUser aau : mRunningAccounts) {
- if (!containsAccountAndUser(oldAccounts, aau.account, aau.userId)) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Account " + aau.account
- + " added, checking sync restore data");
+ AccountAndUser[] accounts = mRunningAccounts;
+ for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
+ if (!containsAccountAndUser(accounts,
+ currentSyncContext.mSyncOperation.target.account,
+ currentSyncContext.mSyncOperation.target.userId)) {
+ Log.d(TAG, "canceling sync since the account is no longer running");
+ sendSyncFinishedOrCanceledMessage(currentSyncContext,
+ null /* no result since this is a cancel */);
+ }
+ }
+
+ if (syncTargets != null) {
+ // On account add, check if there are any settings to be restored.
+ for (AccountAndUser aau : mRunningAccounts) {
+ if (!containsAccountAndUser(oldAccounts, aau.account, aau.userId)) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Account " + aau.account
+ + " added, checking sync restore data");
+ }
+ AccountSyncSettingsBackupHelper.accountAdded(mContext,
+ syncTargets.userId);
+ break;
}
- AccountSyncSettingsBackupHelper.accountAdded(mContext, syncTargets.userId);
- break;
}
}
}
@@ -3442,13 +3447,15 @@
final EndPoint target = op.target;
// Drop the sync if the account of this operation no longer exists.
- AccountAndUser[] accounts = mRunningAccounts;
- if (!containsAccountAndUser(accounts, target.account, target.userId)) {
- if (isLoggable) {
- Slog.v(TAG, " Dropping sync operation: account doesn't exist.");
+ synchronized (mAccountsLock) {
+ AccountAndUser[] accounts = mRunningAccounts;
+ if (!containsAccountAndUser(accounts, target.account, target.userId)) {
+ if (isLoggable) {
+ Slog.v(TAG, " Dropping sync operation: account doesn't exist.");
+ }
+ logAccountError("SYNC_OP_STATE_INVALID: account doesn't exist.");
+ return SYNC_OP_STATE_INVALID_NO_ACCOUNT;
}
- logAccountError("SYNC_OP_STATE_INVALID: account doesn't exist.");
- return SYNC_OP_STATE_INVALID_NO_ACCOUNT;
}
// Drop this sync request if it isn't syncable.
state = computeSyncable(target.account, target.userId, target.provider, true);
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 7e3c1ab..1a28717 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -17,10 +17,14 @@
package com.android.server.devicestate;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+import static android.Manifest.permission.CONTROL_DEVICE_STATE;
import android.annotation.NonNull;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.devicestate.IDeviceStateManager;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.util.IntArray;
import android.util.Slog;
@@ -29,6 +33,7 @@
import com.android.server.SystemService;
import com.android.server.policy.DeviceStatePolicyImpl;
+import java.io.FileDescriptor;
import java.util.Arrays;
/**
@@ -65,15 +70,20 @@
// The current committed device state.
@GuardedBy("mLock")
private int mCommittedState = INVALID_DEVICE_STATE;
- // The device state that is currently pending callback from the policy to be committed.
+ // The device state that is currently awaiting callback from the policy to be committed.
@GuardedBy("mLock")
private int mPendingState = INVALID_DEVICE_STATE;
// Whether or not the policy is currently waiting to be notified of the current pending state.
@GuardedBy("mLock")
private boolean mIsPolicyWaitingForState = false;
// The device state that is currently requested and is next to be configured and committed.
+ // Can be overwritten by an override state value if requested.
@GuardedBy("mLock")
private int mRequestedState = INVALID_DEVICE_STATE;
+ // The most recently requested override state, or INVALID_DEVICE_STATE if no override is
+ // requested.
+ @GuardedBy("mLock")
+ private int mRequestedOverrideState = INVALID_DEVICE_STATE;
public DeviceStateManagerService(@NonNull Context context) {
this(context, new DeviceStatePolicyImpl());
@@ -97,7 +107,6 @@
*
* @see #getPendingState()
*/
- @VisibleForTesting
int getCommittedState() {
synchronized (mLock) {
return mCommittedState;
@@ -119,13 +128,61 @@
* Returns the requested state. The service will configure the device to match the requested
* state when possible.
*/
- @VisibleForTesting
int getRequestedState() {
synchronized (mLock) {
return mRequestedState;
}
}
+ /**
+ * Overrides the current device state with the provided state.
+ *
+ * @return {@code true} if the override state is valid and supported, {@code false} otherwise.
+ */
+ boolean setOverrideState(int overrideState) {
+ if (getContext().checkCallingOrSelfPermission(CONTROL_DEVICE_STATE)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Must hold permission " + CONTROL_DEVICE_STATE);
+ }
+
+ synchronized (mLock) {
+ if (overrideState != INVALID_DEVICE_STATE && !isSupportedStateLocked(overrideState)) {
+ return false;
+ }
+
+ mRequestedOverrideState = overrideState;
+ updatePendingStateLocked();
+ }
+
+ notifyPolicyIfNeeded();
+ return true;
+ }
+
+ /**
+ * Clears an override state set with {@link #setOverrideState(int)}.
+ */
+ void clearOverrideState() {
+ setOverrideState(INVALID_DEVICE_STATE);
+ }
+
+ /**
+ * Returns the current requested override state, or {@link #INVALID_DEVICE_STATE} is no override
+ * state is requested.
+ */
+ int getOverrideState() {
+ synchronized (mLock) {
+ return mRequestedOverrideState;
+ }
+ }
+
+ /** Returns the list of currently supported device states. */
+ int[] getSupportedStates() {
+ synchronized (mLock) {
+ // Copy array to prevent external modification of internal state.
+ return Arrays.copyOf(mSupportedDeviceStates.toArray(), mSupportedDeviceStates.size());
+ }
+ }
+
private void updateSupportedStates(int[] supportedDeviceStates) {
// Must ensure sorted as isSupportedStateLocked() impl uses binary search.
Arrays.sort(supportedDeviceStates, 0, supportedDeviceStates.length);
@@ -135,11 +192,20 @@
if (mRequestedState != INVALID_DEVICE_STATE
&& !isSupportedStateLocked(mRequestedState)) {
// The current requested state is no longer valid. We'll clear it here, though
- // we won't actually update the current state with a call to
- // updatePendingStateLocked() as doing so will not have any effect.
+ // we won't actually update the current state until a callback comes from the
+ // provider with the most recent state.
mRequestedState = INVALID_DEVICE_STATE;
}
+ if (mRequestedOverrideState != INVALID_DEVICE_STATE
+ && !isSupportedStateLocked(mRequestedOverrideState)) {
+ // The current override state is no longer valid. We'll clear it here and update
+ // the committed state if necessary.
+ mRequestedOverrideState = INVALID_DEVICE_STATE;
+ }
+ updatePendingStateLocked();
}
+
+ notifyPolicyIfNeeded();
}
/**
@@ -168,27 +234,34 @@
}
/**
- * Tries to update the current configuring state with the current requested state. Must call
+ * Tries to update the current pending state with the current requested state. Must call
* {@link #notifyPolicyIfNeeded()} to actually notify the policy that the state is being
* changed.
*/
private void updatePendingStateLocked() {
- if (mRequestedState == INVALID_DEVICE_STATE) {
- // No currently requested state.
- return;
- }
-
if (mPendingState != INVALID_DEVICE_STATE) {
// Have pending state, can not configure a new state until the state is committed.
return;
}
- if (mRequestedState == mCommittedState) {
- // No need to notify the policy as the committed state matches the requested state.
+ int stateToConfigure;
+ if (mRequestedOverrideState != INVALID_DEVICE_STATE) {
+ stateToConfigure = mRequestedOverrideState;
+ } else {
+ stateToConfigure = mRequestedState;
+ }
+
+ if (stateToConfigure == INVALID_DEVICE_STATE) {
+ // No currently requested state.
return;
}
- mPendingState = mRequestedState;
+ if (stateToConfigure == mCommittedState) {
+ // The state requesting to be committed already matches the current committed state.
+ return;
+ }
+
+ mPendingState = stateToConfigure;
mIsPolicyWaitingForState = true;
}
@@ -271,6 +344,11 @@
/** Implementation of {@link IDeviceStateManager} published as a binder service. */
private final class BinderService extends IDeviceStateManager.Stub {
-
+ @Override // Binder call
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver result) {
+ new DeviceStateManagerShellCommand(DeviceStateManagerService.this)
+ .exec(this, in, out, err, args, callback, result);
+ }
}
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
new file mode 100644
index 0000000..cf3b297
--- /dev/null
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
@@ -0,0 +1,128 @@
+/*
+ * 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.devicestate;
+
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+/**
+ * ShellCommands for {@link DeviceStateManagerService}.
+ *
+ * Use with {@code adb shell cmd device_state ...}.
+ */
+public class DeviceStateManagerShellCommand extends ShellCommand {
+ private final DeviceStateManagerService mInternal;
+
+ public DeviceStateManagerShellCommand(DeviceStateManagerService service) {
+ mInternal = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ final PrintWriter pw = getOutPrintWriter();
+
+ switch (cmd) {
+ case "state":
+ return runState(pw);
+ case "print-states":
+ return runPrintStates(pw);
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ private void printState(PrintWriter pw) {
+ int committedState = mInternal.getCommittedState();
+ int requestedState = mInternal.getRequestedState();
+ int requestedOverrideState = mInternal.getOverrideState();
+
+ if (committedState == INVALID_DEVICE_STATE) {
+ pw.println("Device state: (invalid)");
+ } else {
+ pw.println("Device state: " + committedState);
+ }
+
+ if (requestedOverrideState != INVALID_DEVICE_STATE) {
+ pw.println("----------------------");
+ if (requestedState == INVALID_DEVICE_STATE) {
+ pw.println("Base state: (invalid)");
+ } else {
+ pw.println("Base state: " + requestedState);
+ }
+ pw.println("Override state: " + committedState);
+ }
+ }
+
+ private int runState(PrintWriter pw) {
+ final String nextArg = getNextArg();
+ if (nextArg == null) {
+ printState(pw);
+ } else if ("reset".equals(nextArg)) {
+ mInternal.clearOverrideState();
+ } else {
+ int requestedState;
+ try {
+ requestedState = Integer.parseInt(nextArg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: requested state should be an integer");
+ return -1;
+ }
+
+ boolean success = mInternal.setOverrideState(requestedState);
+ if (!success) {
+ getErrPrintWriter().println("Error: failed to set override state. Run:");
+ getErrPrintWriter().println("");
+ getErrPrintWriter().println(" print-states");
+ getErrPrintWriter().println("");
+ getErrPrintWriter().println("to get the list of currently supported device states");
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ private int runPrintStates(PrintWriter pw) {
+ int[] states = mInternal.getSupportedStates();
+ pw.print("Supported states: [ ");
+ for (int i = 0; i < states.length; i++) {
+ pw.print(states[i]);
+ if (i < states.length - 1) {
+ pw.print(", ");
+ }
+ }
+ pw.println(" ]");
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Device state manager (device_state) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" state [reset|OVERRIDE_DEVICE_STATE]");
+ pw.println(" Return or override device state.");
+ pw.println(" print-states");
+ pw.println(" Return list of currently supported device states.");
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index a9e8719..8980de1 100755
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -339,7 +339,8 @@
// This is to manager CEC device separately in case they don't have address.
if (mIsTvDevice) {
- tv().updateCecSwitchInfo(current.mLogicalAddress, current.mDeviceType,
+ localDevice().mService.getHdmiCecNetwork().updateCecSwitchInfo(current.mLogicalAddress,
+ current.mDeviceType,
current.mPhysicalAddress);
}
increaseProcessedDeviceCount();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
new file mode 100644
index 0000000..652bf5a
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -0,0 +1,350 @@
+/*
+ * 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.hdmi;
+
+import static android.hardware.hdmi.HdmiControlManager.CecSettingName;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.hdmi.HdmiControlManager;
+import android.os.Environment;
+import android.os.SystemProperties;
+import android.provider.Settings.Global;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.hdmi.cec.config.CecSettings;
+import com.android.server.hdmi.cec.config.Setting;
+import com.android.server.hdmi.cec.config.Value;
+import com.android.server.hdmi.cec.config.XmlParser;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+
+/**
+ * The {@link HdmiCecConfig} class is used for getting information about
+ * available HDMI CEC settings.
+ */
+public class HdmiCecConfig {
+ private static final String TAG = "HdmiCecConfig";
+
+ private static final String ETC_DIR = "etc";
+ private static final String CONFIG_FILE = "cec_config.xml";
+
+ @IntDef({
+ STORAGE_SYSPROPS,
+ STORAGE_GLOBAL_SETTINGS,
+ })
+ private @interface Storage {}
+
+ private static final int STORAGE_SYSPROPS = 0;
+ private static final int STORAGE_GLOBAL_SETTINGS = 1;
+
+ /**
+ * System property key for Power State Change on Active Source Lost.
+ */
+ public static final String SYSPROP_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST =
+ "ro.hdmi.cec.source.power_state_change_on_active_source_lost";
+
+ /**
+ * System property key for Audio Mode Muting.
+ */
+ public static final String SYSPROP_SYSTEM_AUDIO_MODE_MUTING =
+ "ro.hdmi.cec.audio.system_audio_mode_muting.enabled";
+
+ @NonNull private final Context mContext;
+ @NonNull private final StorageAdapter mStorageAdapter;
+ @Nullable private final CecSettings mProductConfig;
+ @Nullable private final CecSettings mVendorOverride;
+
+ /**
+ * Setting storage input/output helper class.
+ */
+ public static class StorageAdapter {
+ /**
+ * Read the value from a system property.
+ * Returns the given default value if the system property is not set.
+ */
+ public String retrieveSystemProperty(@NonNull String storageKey,
+ @NonNull String defaultValue) {
+ return SystemProperties.get(storageKey, defaultValue);
+ }
+
+ /**
+ * Write the value to a system property.
+ */
+ public void storeSystemProperty(@NonNull String storageKey,
+ @NonNull String value) {
+ SystemProperties.set(storageKey, value);
+ }
+
+ /**
+ * Read the value from a global setting.
+ * Returns the given default value if the system property is not set.
+ */
+ public String retrieveGlobalSetting(@NonNull Context context,
+ @NonNull String storageKey,
+ @NonNull String defaultValue) {
+ String value = Global.getString(context.getContentResolver(), storageKey);
+ return value != null ? value : defaultValue;
+ }
+
+ /**
+ * Write the value to a global setting.
+ */
+ public void storeGlobalSetting(@NonNull Context context,
+ @NonNull String storageKey,
+ @NonNull String value) {
+ Global.putString(context.getContentResolver(), storageKey, value);
+ }
+ }
+
+ @VisibleForTesting
+ HdmiCecConfig(@NonNull Context context,
+ @NonNull StorageAdapter storageAdapter,
+ @Nullable CecSettings productConfig,
+ @Nullable CecSettings vendorOverride) {
+ mContext = context;
+ mStorageAdapter = storageAdapter;
+ mProductConfig = productConfig;
+ mVendorOverride = vendorOverride;
+ if (mProductConfig == null) {
+ Slog.i(TAG, "CEC master configuration XML missing.");
+ }
+ if (mVendorOverride == null) {
+ Slog.i(TAG, "CEC OEM configuration override XML missing.");
+ }
+ }
+
+ HdmiCecConfig(@NonNull Context context) {
+ this(context, new StorageAdapter(),
+ readSettingsFromFile(Environment.buildPath(Environment.getProductDirectory(),
+ ETC_DIR, CONFIG_FILE)),
+ readSettingsFromFile(Environment.buildPath(Environment.getVendorDirectory(),
+ ETC_DIR, CONFIG_FILE)));
+ }
+
+ @Nullable
+ private static CecSettings readSettingsFromFile(@NonNull File file) {
+ if (!file.exists()) {
+ return null;
+ }
+ if (!file.isFile()) {
+ Slog.e(TAG, "CEC configuration is not a file: " + file + ", skipping.");
+ return null;
+ }
+ try (InputStream in = new BufferedInputStream(new FileInputStream(file))) {
+ return XmlParser.read(in);
+ } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+ Slog.e(TAG, "Encountered an error while reading/parsing CEC config file: " + file, e);
+ }
+ return null;
+ }
+
+ @Nullable
+ private Setting getSetting(@NonNull String name) {
+ if (mProductConfig == null) {
+ return null;
+ }
+ if (mVendorOverride != null) {
+ // First read from the vendor override.
+ for (Setting setting : mVendorOverride.getSetting()) {
+ if (setting.getName().equals(name)) {
+ return setting;
+ }
+ }
+ }
+ // If not found, try the product config.
+ for (Setting setting : mProductConfig.getSetting()) {
+ if (setting.getName().equals(name)) {
+ return setting;
+ }
+ }
+ return null;
+ }
+
+ @Storage
+ private int getStorage(@NonNull Setting setting) {
+ switch (setting.getName()) {
+ case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED:
+ return STORAGE_GLOBAL_SETTINGS;
+ case HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP:
+ return STORAGE_GLOBAL_SETTINGS;
+ case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
+ return STORAGE_SYSPROPS;
+ case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
+ return STORAGE_SYSPROPS;
+ default:
+ throw new RuntimeException("Invalid CEC setting '" + setting.getName()
+ + "' storage.");
+ }
+ }
+
+ private String getStorageKey(@NonNull Setting setting) {
+ switch (setting.getName()) {
+ case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED:
+ return Global.HDMI_CONTROL_ENABLED;
+ case HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP:
+ return Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP;
+ case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
+ return SYSPROP_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST;
+ case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
+ return SYSPROP_SYSTEM_AUDIO_MODE_MUTING;
+ default:
+ throw new RuntimeException("Invalid CEC setting '" + setting.getName()
+ + "' storage key.");
+ }
+ }
+
+ private String retrieveValue(@NonNull Setting setting) {
+ @Storage int storage = getStorage(setting);
+ String storageKey = getStorageKey(setting);
+ if (storage == STORAGE_SYSPROPS) {
+ Slog.d(TAG, "Reading '" + storageKey + "' sysprop.");
+ return mStorageAdapter.retrieveSystemProperty(storageKey,
+ setting.getDefaultValue().getStringValue());
+ } else if (storage == STORAGE_GLOBAL_SETTINGS) {
+ Slog.d(TAG, "Reading '" + storageKey + "' global setting.");
+ return mStorageAdapter.retrieveGlobalSetting(mContext, storageKey,
+ setting.getDefaultValue().getStringValue());
+ }
+ return null;
+ }
+
+ private void storeValue(@NonNull Setting setting, @NonNull String value) {
+ @Storage int storage = getStorage(setting);
+ String storageKey = getStorageKey(setting);
+ if (storage == STORAGE_SYSPROPS) {
+ Slog.d(TAG, "Setting '" + storageKey + "' sysprop.");
+ mStorageAdapter.storeSystemProperty(storageKey, value);
+ } else if (storage == STORAGE_GLOBAL_SETTINGS) {
+ Slog.d(TAG, "Setting '" + storageKey + "' global setting.");
+ mStorageAdapter.storeGlobalSetting(mContext, storageKey, value);
+ }
+ }
+
+ /**
+ * Returns a list of all settings based on the XML metadata.
+ */
+ public @CecSettingName List<String> getAllSettings() {
+ if (mProductConfig == null) {
+ return new ArrayList<String>();
+ }
+ List<String> allSettings = new ArrayList<String>();
+ for (Setting setting : mProductConfig.getSetting()) {
+ allSettings.add(setting.getName());
+ }
+ return allSettings;
+ }
+
+ /**
+ * Returns a list of user-modifiable settings based on the XML metadata.
+ */
+ public @CecSettingName List<String> getUserSettings() {
+ if (mProductConfig == null) {
+ return new ArrayList<String>();
+ }
+ Set<String> userSettings = new HashSet<String>();
+ // First read from the product config.
+ for (Setting setting : mProductConfig.getSetting()) {
+ if (setting.getUserConfigurable()) {
+ userSettings.add(setting.getName());
+ }
+ }
+ if (mVendorOverride != null) {
+ // Next either add or remove based on the vendor override.
+ for (Setting setting : mVendorOverride.getSetting()) {
+ if (setting.getUserConfigurable()) {
+ userSettings.add(setting.getName());
+ } else {
+ userSettings.remove(setting.getName());
+ }
+ }
+ }
+ return new ArrayList(userSettings);
+ }
+
+ /**
+ * For a given setting name returns values that are allowed for that setting.
+ */
+ public List<String> getAllowedValues(@NonNull @CecSettingName String name) {
+ Setting setting = getSetting(name);
+ if (setting == null) {
+ throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+ }
+ List<String> allowedValues = new ArrayList<String>();
+ for (Value allowedValue : setting.getAllowedValues().getValue()) {
+ allowedValues.add(allowedValue.getStringValue());
+ }
+ return allowedValues;
+ }
+
+ /**
+ * For a given setting name returns the default value for that setting.
+ */
+ public String getDefaultValue(@NonNull @CecSettingName String name) {
+ Setting setting = getSetting(name);
+ if (setting == null) {
+ throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+ }
+ return getSetting(name).getDefaultValue().getStringValue();
+ }
+
+ /**
+ * For a given setting name returns the current value of that setting.
+ */
+ public String getValue(@NonNull @CecSettingName String name) {
+ Setting setting = getSetting(name);
+ if (setting == null) {
+ throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+ }
+ Slog.d(TAG, "Getting CEC setting value '" + name + "'.");
+ return retrieveValue(setting);
+ }
+
+ /**
+ * For a given setting name and value sets the current value of that setting.
+ */
+ public void setValue(@NonNull @CecSettingName String name, @NonNull String value) {
+ Setting setting = getSetting(name);
+ if (setting == null) {
+ throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+ }
+ if (!setting.getUserConfigurable()) {
+ throw new IllegalArgumentException("Updating CEC setting '" + name + "' prohibited.");
+ }
+ if (!getAllowedValues(name).contains(value)) {
+ throw new IllegalArgumentException("Invalid CEC setting '" + name
+ + "' value: '" + value + "'.");
+ }
+ Slog.d(TAG, "Updating CEC setting '" + name + "' to '" + value + "'.");
+ storeValue(setting, value);
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 96679c3..efe7302 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -32,7 +32,6 @@
import android.os.RemoteException;
import android.stats.hdmi.HdmiStatsEnums;
import android.util.Slog;
-import android.util.SparseArray;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
@@ -97,7 +96,7 @@
private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
@Override
public boolean test(Integer address) {
- return !isAllocatedLocalDeviceAddress(address);
+ return !mService.getHdmiCecNetwork().isAllocatedLocalDeviceAddress(address);
}
};
@@ -118,9 +117,6 @@
private final HdmiControlService mService;
- // Stores the local CEC devices in the system. Device type is used for key.
- private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
-
// Stores recent CEC messages and HDMI Hotplug event history for debugging purpose.
private final ArrayBlockingQueue<Dumpable> mMessageHistory =
new ArrayBlockingQueue<>(MAX_HDMI_MESSAGE_HISTORY);
@@ -173,12 +169,6 @@
nativeWrapper.setCallback(new HdmiCecCallback());
}
- @ServiceThreadOnly
- void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
- assertRunOnServiceThread();
- mLocalDevices.put(deviceType, device);
- }
-
/**
* Allocate a new logical address of the given device type. Allocated
* address will be reported through {@link AllocateAddressCallback}.
@@ -269,17 +259,6 @@
}
/**
- * Return the locally hosted logical device of a given type.
- *
- * @param deviceType logical device type
- * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
- * otherwise null.
- */
- HdmiCecLocalDevice getLocalDevice(int deviceType) {
- return mLocalDevices.get(deviceType);
- }
-
- /**
* Add a new logical address to the device. Device's HW should be notified
* when a new logical address is assigned to a device, so that it can accept
* a command having available destinations.
@@ -307,18 +286,9 @@
@ServiceThreadOnly
void clearLogicalAddress() {
assertRunOnServiceThread();
- for (int i = 0; i < mLocalDevices.size(); ++i) {
- mLocalDevices.valueAt(i).clearAddress();
- }
mNativeWrapperImpl.nativeClearLogicalAddress();
}
- @ServiceThreadOnly
- void clearLocalDevices() {
- assertRunOnServiceThread();
- mLocalDevices.clear();
- }
-
/**
* Return the physical address of the device.
*
@@ -428,17 +398,6 @@
runDevicePolling(sourceAddress, pollingCandidates, retryCount, callback, allocated);
}
- /**
- * Return a list of all {@link HdmiCecLocalDevice}s.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- */
- @ServiceThreadOnly
- List<HdmiCecLocalDevice> getLocalDeviceList() {
- assertRunOnServiceThread();
- return HdmiUtils.sparseArrayToList(mLocalDevices);
- }
-
private List<Integer> pickPollCandidates(int pickStrategy) {
int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
Predicate<Integer> pickPredicate = null;
@@ -475,17 +434,6 @@
}
@ServiceThreadOnly
- private boolean isAllocatedLocalDeviceAddress(int address) {
- assertRunOnServiceThread();
- for (int i = 0; i < mLocalDevices.size(); ++i) {
- if (mLocalDevices.valueAt(i).isAddressOf(address)) {
- return true;
- }
- }
- return false;
- }
-
- @ServiceThreadOnly
private void runDevicePolling(final int sourceAddress,
final List<Integer> candidates, final int retryCount,
final DevicePollingCallback callback, final List<Integer> allocated) {
@@ -578,7 +526,7 @@
if (address == Constants.ADDR_BROADCAST) {
return true;
}
- return isAllocatedLocalDeviceAddress(address);
+ return mService.getHdmiCecNetwork().isAllocatedLocalDeviceAddress(address);
}
@ServiceThreadOnly
@@ -682,7 +630,7 @@
private int incomingMessageDirection(int srcAddress, int dstAddress) {
boolean sourceIsLocal = false;
boolean destinationIsLocal = false;
- for (HdmiCecLocalDevice localDevice : getLocalDeviceList()) {
+ for (HdmiCecLocalDevice localDevice : mService.getHdmiCecNetwork().getLocalDeviceList()) {
int logicalAddress = localDevice.getDeviceInfo().getLogicalAddress();
if (logicalAddress == srcAddress) {
sourceIsLocal = true;
@@ -731,24 +679,9 @@
}
void dump(final IndentingPrintWriter pw) {
- final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-
- for (int i = 0; i < mLocalDevices.size(); ++i) {
- pw.println("HdmiCecLocalDevice #" + mLocalDevices.keyAt(i) + ":");
- pw.increaseIndent();
- mLocalDevices.valueAt(i).dump(pw);
-
- pw.println("Active Source history:");
- pw.increaseIndent();
- for (Dumpable activeSourceEvent : mLocalDevices.valueAt(i).getActiveSourceHistory()) {
- activeSourceEvent.dump(pw, sdf);
- }
- pw.decreaseIndent();
- pw.decreaseIndent();
- }
-
pw.println("CEC message history:");
pw.increaseIndent();
+ final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
for (Dumpable record : mMessageHistory) {
record.dump(pw, sdf);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 946fb0d..62a67b6 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -16,6 +16,7 @@
package com.android.server.hdmi;
+import android.annotation.CallSuper;
import android.annotation.Nullable;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
@@ -471,8 +472,27 @@
return false;
}
+ @CallSuper
protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
- return false;
+ // <Report Physical Address> is also handled in HdmiCecNetwork to update the local network
+ // state
+
+ int address = message.getSource();
+
+ // Ignore if [Device Discovery Action] is going on.
+ if (hasAction(DeviceDiscoveryAction.class)) {
+ Slog.i(TAG, "Ignored while Device Discovery Action is in progress: " + message);
+ return true;
+ }
+
+ HdmiDeviceInfo cecDeviceInfo = mService.getHdmiCecNetwork().getCecDeviceInfo(address);
+ // If no non-default display name is available for the device, request the devices OSD name.
+ if (cecDeviceInfo.getDisplayName().equals(HdmiUtils.getDefaultDeviceName(address))) {
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildGiveOsdNameCommand(mAddress, address));
+ }
+
+ return true;
}
protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
@@ -708,7 +728,7 @@
}
protected boolean handleSetOsdName(HdmiCecMessage message) {
- // The default behavior of <Set Osd Name> is doing nothing.
+ // <Set OSD name> is also handled in HdmiCecNetwork to update the local network state
return true;
}
@@ -724,7 +744,8 @@
}
protected boolean handleReportPowerStatus(HdmiCecMessage message) {
- return false;
+ // <Report Power Status> is also handled in HdmiCecNetwork to update the local network state
+ return true;
}
protected boolean handleTimerStatus(HdmiCecMessage message) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 29bdd6c..fe4fd38 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -37,7 +37,6 @@
import android.provider.Settings.Global;
import android.sysprop.HdmiProperties;
import android.util.Slog;
-import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -54,15 +53,12 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
-
/**
* Represent a logical device of type {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM} residing in Android
* system.
@@ -104,14 +100,6 @@
@GuardedBy("mLock")
private final HashMap<String, HdmiDeviceInfo> mTvInputsToDeviceInfo = new HashMap<>();
- // Copy of mDeviceInfos to guarantee thread-safety.
- @GuardedBy("mLock")
- private List<HdmiDeviceInfo> mSafeAllDeviceInfos = Collections.emptyList();
-
- // Map-like container of all cec devices.
- // device id is used as key of container.
- private final SparseArray<HdmiDeviceInfo> mDeviceInfos = new SparseArray<>();
-
// Message buffer used to buffer selected messages to process later. <Active Source>
// from a source device, for instance, needs to be buffered if the device is not
// discovered yet. The buffered commands are taken out and when they are ready to
@@ -187,135 +175,6 @@
return info != null;
}
- /**
- * Called when a device is newly added or a new device is detected or
- * an existing device is updated.
- *
- * @param info device info of a new device.
- */
- @ServiceThreadOnly
- final void addCecDevice(HdmiDeviceInfo info) {
- assertRunOnServiceThread();
- HdmiDeviceInfo old = addDeviceInfo(info);
- if (info.getPhysicalAddress() == mService.getPhysicalAddress()) {
- // The addition of the device itself should not be notified.
- // Note that different logical address could still be the same local device.
- return;
- }
- if (old == null) {
- invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
- } else if (!old.equals(info)) {
- invokeDeviceEventListener(old, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
- invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
- }
- }
-
- /**
- * Called when a device is removed or removal of device is detected.
- *
- * @param address a logical address of a device to be removed
- */
- @ServiceThreadOnly
- final void removeCecDevice(int address) {
- assertRunOnServiceThread();
- HdmiDeviceInfo info = removeDeviceInfo(HdmiDeviceInfo.idForCecDevice(address));
-
- mCecMessageCache.flushMessagesFrom(address);
- invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
- }
-
- /**
- * Called when a device is updated.
- *
- * @param info device info of the updating device.
- */
- @ServiceThreadOnly
- final void updateCecDevice(HdmiDeviceInfo info) {
- assertRunOnServiceThread();
- HdmiDeviceInfo old = addDeviceInfo(info);
-
- if (old == null) {
- invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
- } else if (!old.equals(info)) {
- invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
- }
- }
-
- /**
- * Add a new {@link HdmiDeviceInfo}. It returns old device info which has the same
- * logical address as new device info's.
- *
- * @param deviceInfo a new {@link HdmiDeviceInfo} to be added.
- * @return {@code null} if it is new device. Otherwise, returns old {@HdmiDeviceInfo}
- * that has the same logical address as new one has.
- */
- @ServiceThreadOnly
- @VisibleForTesting
- protected HdmiDeviceInfo addDeviceInfo(HdmiDeviceInfo deviceInfo) {
- assertRunOnServiceThread();
- mService.checkLogicalAddressConflictAndReallocate(deviceInfo.getLogicalAddress());
- HdmiDeviceInfo oldDeviceInfo = getCecDeviceInfo(deviceInfo.getLogicalAddress());
- if (oldDeviceInfo != null) {
- removeDeviceInfo(deviceInfo.getId());
- }
- mDeviceInfos.append(deviceInfo.getId(), deviceInfo);
- updateSafeDeviceInfoList();
- return oldDeviceInfo;
- }
-
- /**
- * Remove a device info corresponding to the given {@code logicalAddress}.
- * It returns removed {@link HdmiDeviceInfo} if exists.
- *
- * @param id id of device to be removed
- * @return removed {@link HdmiDeviceInfo} it exists. Otherwise, returns {@code null}
- */
- @ServiceThreadOnly
- private HdmiDeviceInfo removeDeviceInfo(int id) {
- assertRunOnServiceThread();
- HdmiDeviceInfo deviceInfo = mDeviceInfos.get(id);
- if (deviceInfo != null) {
- mDeviceInfos.remove(id);
- }
- updateSafeDeviceInfoList();
- return deviceInfo;
- }
-
- /**
- * Return a {@link HdmiDeviceInfo} corresponding to the given {@code logicalAddress}.
- *
- * @param logicalAddress logical address of the device to be retrieved
- * @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
- * Returns null if no logical address matched
- */
- @ServiceThreadOnly
- HdmiDeviceInfo getCecDeviceInfo(int logicalAddress) {
- assertRunOnServiceThread();
- return mDeviceInfos.get(HdmiDeviceInfo.idForCecDevice(logicalAddress));
- }
-
- @ServiceThreadOnly
- private void updateSafeDeviceInfoList() {
- assertRunOnServiceThread();
- List<HdmiDeviceInfo> copiedDevices = HdmiUtils.sparseArrayToList(mDeviceInfos);
- synchronized (mLock) {
- mSafeAllDeviceInfos = copiedDevices;
- }
- }
-
- @GuardedBy("mLock")
- List<HdmiDeviceInfo> getSafeCecDevicesLocked() {
- ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
- for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
- infoList.add(info);
- }
- return infoList;
- }
-
- private void invokeDeviceEventListener(HdmiDeviceInfo info, int status) {
- mService.invokeDeviceEventListeners(info, status);
- }
-
@Override
@ServiceThreadOnly
void onHotplug(int portId, boolean connected) {
@@ -342,7 +201,7 @@
}
// Update with TIF on the device removal. TIF callback will update
// mPortIdToTvInputs and mPortIdToTvInputs.
- removeCecDevice(info.getLogicalAddress());
+ mService.getHdmiCecNetwork().removeCecDevice(this, info.getLogicalAddress());
}
}
@@ -399,7 +258,7 @@
boolean lastSystemAudioControlStatus =
SystemProperties.getBoolean(Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL, true);
systemAudioControlOnPowerOn(systemAudioControlOnPowerOnProp, lastSystemAudioControlStatus);
- clearDeviceInfoList();
+ mService.getHdmiCecNetwork().clearDeviceList();
launchDeviceDiscovery();
startQueuedActions();
}
@@ -458,7 +317,7 @@
// If the new Active Source is under the current device, check if the device info and the TV
// input is ready to switch to the new Active Source. If not ready, buffer the cec command
// to handle later when the device is ready.
- HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress);
+ HdmiDeviceInfo info = mService.getHdmiCecNetwork().getCecDeviceInfo(logicalAddress);
if (info == null) {
HdmiLogger.debug("Device info %X not found; buffering the command", logicalAddress);
mDelayedMessageBuffer.add(message);
@@ -474,79 +333,6 @@
@Override
@ServiceThreadOnly
- protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
- assertRunOnServiceThread();
- int path = HdmiUtils.twoBytesToInt(message.getParams());
- int address = message.getSource();
- int type = message.getParams()[2];
-
- // Ignore if [Device Discovery Action] is going on.
- if (hasAction(DeviceDiscoveryAction.class)) {
- Slog.i(TAG, "Ignored while Device Discovery Action is in progress: " + message);
- return true;
- }
-
- // Update the device info with TIF, note that the same device info could have added in
- // device discovery and we do not want to override it with default OSD name. Therefore we
- // need the following check to skip redundant device info updating.
- HdmiDeviceInfo oldDevice = getCecDeviceInfo(address);
- if (oldDevice == null || oldDevice.getPhysicalAddress() != path) {
- addCecDevice(new HdmiDeviceInfo(
- address, path, mService.pathToPortId(path), type,
- Constants.UNKNOWN_VENDOR_ID, ""));
- // if we are adding a new device info, send out a give osd name command
- // to update the name of the device in TIF
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildGiveOsdNameCommand(mAddress, address));
- return true;
- }
-
- Slog.w(TAG, "Device info exists. Not updating on Physical Address.");
- return true;
- }
-
- @Override
- protected boolean handleReportPowerStatus(HdmiCecMessage command) {
- int newStatus = command.getParams()[0] & 0xFF;
- updateDevicePowerStatus(command.getSource(), newStatus);
- return true;
- }
-
- @Override
- @ServiceThreadOnly
- protected boolean handleSetOsdName(HdmiCecMessage message) {
- int source = message.getSource();
- String osdName;
- HdmiDeviceInfo deviceInfo = getCecDeviceInfo(source);
- // If the device is not in device list, ignore it.
- if (deviceInfo == null) {
- Slog.i(TAG, "No source device info for <Set Osd Name>." + message);
- return true;
- }
- try {
- osdName = new String(message.getParams(), "US-ASCII");
- } catch (UnsupportedEncodingException e) {
- Slog.e(TAG, "Invalid <Set Osd Name> request:" + message, e);
- return true;
- }
-
- if (deviceInfo.getDisplayName() != null
- && deviceInfo.getDisplayName().equals(osdName)) {
- Slog.d(TAG, "Ignore incoming <Set Osd Name> having same osd name:" + message);
- return true;
- }
-
- Slog.d(TAG, "Updating device OSD name from "
- + deviceInfo.getDisplayName()
- + " to " + osdName);
- updateCecDevice(new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
- deviceInfo.getPhysicalAddress(), deviceInfo.getPortId(),
- deviceInfo.getDeviceType(), deviceInfo.getVendorId(), osdName));
- return true;
- }
-
- @Override
- @ServiceThreadOnly
protected boolean handleInitiateArc(HdmiCecMessage message) {
assertRunOnServiceThread();
// TODO(amyjojo): implement initiate arc handler
@@ -864,14 +650,9 @@
!= HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE) {
return true;
}
- boolean isDeviceInCecDeviceList = false;
- for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) {
- if (info.getPhysicalAddress() == sourcePhysicalAddress) {
- isDeviceInCecDeviceList = true;
- break;
- }
- }
- if (!isDeviceInCecDeviceList) {
+ HdmiDeviceInfo safeDeviceInfoByPath =
+ mService.getHdmiCecNetwork().getSafeDeviceInfoByPath(sourcePhysicalAddress);
+ if (safeDeviceInfoByPath == null) {
switchInputOnReceivingNewActivePath(sourcePhysicalAddress);
}
}
@@ -1345,24 +1126,6 @@
routeToInputFromPortId(getRoutingPort());
}
- protected void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) {
- HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress);
- if (info == null) {
- Slog.w(TAG, "Can not update power status of non-existing device:" + logicalAddress);
- return;
- }
-
- if (info.getDevicePowerStatus() == newPowerStatus) {
- return;
- }
-
- HdmiDeviceInfo newInfo = HdmiUtils.cloneHdmiDeviceInfo(info, newPowerStatus);
- // addDeviceInfo replaces old device info with new one if exists.
- addDeviceInfo(newInfo);
-
- invokeDeviceEventListener(newInfo, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
- }
-
@ServiceThreadOnly
private void launchDeviceDiscovery() {
assertRunOnServiceThread();
@@ -1375,27 +1138,13 @@
@Override
public void onDeviceDiscoveryDone(List<HdmiDeviceInfo> deviceInfos) {
for (HdmiDeviceInfo info : deviceInfos) {
- addCecDevice(info);
+ mService.getHdmiCecNetwork().addCecDevice(info);
}
}
});
addAndStartAction(action);
}
- // Clear all device info.
- @ServiceThreadOnly
- private void clearDeviceInfoList() {
- assertRunOnServiceThread();
- for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) {
- if (info.getPhysicalAddress() == mService.getPhysicalAddress()) {
- continue;
- }
- invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
- }
- mDeviceInfos.clear();
- updateSafeDeviceInfoList();
- }
-
@Override
protected void dump(IndentingPrintWriter pw) {
pw.println("HdmiCecLocalDeviceAudioSystem:");
@@ -1409,7 +1158,6 @@
pw.println("mLocalActivePort: " + getLocalActivePort());
HdmiUtils.dumpMap(pw, "mPortIdToTvInputs:", mPortIdToTvInputs);
HdmiUtils.dumpMap(pw, "mTvInputsToDeviceInfo:", mTvInputsToDeviceInfo);
- HdmiUtils.dumpSparseArray(pw, "mDeviceInfos:", mDeviceInfos);
pw.decreaseIndent();
super.dump(pw);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 0325ad9..93cdca2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -42,9 +42,7 @@
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager.TvInputCallback;
import android.provider.Settings.Global;
-import android.util.ArraySet;
import android.util.Slog;
-import android.util.SparseArray;
import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
@@ -53,13 +51,8 @@
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
/**
@@ -95,37 +88,18 @@
@GuardedBy("mLock")
private boolean mSystemAudioMute = false;
- // Copy of mDeviceInfos to guarantee thread-safety.
- @GuardedBy("mLock")
- private List<HdmiDeviceInfo> mSafeAllDeviceInfos = Collections.emptyList();
- // All external cec input(source) devices. Does not include system audio device.
- @GuardedBy("mLock")
- private List<HdmiDeviceInfo> mSafeExternalInputs = Collections.emptyList();
-
- // Map-like container of all cec devices including local ones.
- // device id is used as key of container.
- // This is not thread-safe. For external purpose use mSafeDeviceInfos.
- private final SparseArray<HdmiDeviceInfo> mDeviceInfos = new SparseArray<>();
-
// If true, TV going to standby mode puts other devices also to standby.
private boolean mAutoDeviceOff;
// If true, TV wakes itself up when receiving <Text/Image View On>.
private boolean mAutoWakeup;
- // List of the logical address of local CEC devices. Unmodifiable, thread-safe.
- private List<Integer> mLocalDeviceAddresses;
-
private final HdmiCecStandbyModeHandler mStandbyHandler;
// If true, do not do routing control/send active source for internal source.
// Set to true when the device was woken up by <Text/Image View On>.
private boolean mSkipRoutingControl;
- // Set of physical addresses of CEC switches on the CEC bus. Managed independently from
- // other CEC devices since they might not have logical address.
- private final ArraySet<Integer> mCecSwitches = new ArraySet<Integer>();
-
// Message buffer used to buffer selected messages to process later. <Active Source>
// from a source device, for instance, needs to be buffered if the device is not
// discovered yet. The buffered commands are taken out and when they are ready to
@@ -205,12 +179,12 @@
mAddress, mService.getPhysicalAddress(), mDeviceType));
mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
mAddress, mService.getVendorId()));
- mCecSwitches.add(mService.getPhysicalAddress()); // TV is a CEC switch too.
+ mService.getHdmiCecNetwork().addCecSwitch(
+ mService.getHdmiCecNetwork().getPhysicalAddress()); // TV is a CEC switch too.
mTvInputs.clear();
mSkipRoutingControl = (reason == HdmiControlService.INITIATED_BY_WAKE_UP_MESSAGE);
launchRoutingControl(reason != HdmiControlService.INITIATED_BY_ENABLE_CEC &&
reason != HdmiControlService.INITIATED_BY_BOOT_UP);
- mLocalDeviceAddresses = initLocalDeviceAddresses();
resetSelectRequestBuffer();
launchDeviceDiscovery();
startQueuedActions();
@@ -220,17 +194,6 @@
}
@ServiceThreadOnly
- private List<Integer> initLocalDeviceAddresses() {
- assertRunOnServiceThread();
- List<Integer> addresses = new ArrayList<>();
- for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) {
- addresses.add(device.getDeviceInfo().getLogicalAddress());
- }
- return Collections.unmodifiableList(addresses);
- }
-
-
- @ServiceThreadOnly
public void setSelectRequestBuffer(SelectRequestBuffer requestBuffer) {
assertRunOnServiceThread();
mSelectRequestBuffer = requestBuffer;
@@ -272,7 +235,7 @@
@ServiceThreadOnly
void deviceSelect(int id, IHdmiControlCallback callback) {
assertRunOnServiceThread();
- HdmiDeviceInfo targetDevice = mDeviceInfos.get(id);
+ HdmiDeviceInfo targetDevice = mService.getHdmiCecNetwork().getDeviceInfo(id);
if (targetDevice == null) {
invokeCallback(callback, HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE);
return;
@@ -335,7 +298,8 @@
}
setActiveSource(newActive, caller);
int logicalAddress = newActive.logicalAddress;
- if (getCecDeviceInfo(logicalAddress) != null && logicalAddress != mAddress) {
+ if (mService.getHdmiCecNetwork().getCecDeviceInfo(logicalAddress) != null
+ && logicalAddress != mAddress) {
if (mService.pathToPortId(newActive.physicalAddress) == getActivePortId()) {
setPrevPortId(getActivePortId());
}
@@ -374,7 +338,8 @@
// Show OSD port change banner
if (notifyInputChange) {
ActiveSource activeSource = getActiveSource();
- HdmiDeviceInfo info = getCecDeviceInfo(activeSource.logicalAddress);
+ HdmiDeviceInfo info = mService.getHdmiCecNetwork().getCecDeviceInfo(
+ activeSource.logicalAddress);
if (info == null) {
info = mService.getDeviceInfoByPort(getActivePortId());
if (info == null) {
@@ -442,7 +407,7 @@
if (getActiveSource().isValid()) {
return getActiveSource().logicalAddress;
}
- HdmiDeviceInfo info = getDeviceInfoByPath(getActivePath());
+ HdmiDeviceInfo info = mService.getHdmiCecNetwork().getDeviceInfoByPath(getActivePath());
if (info != null) {
return info.getLogicalAddress();
}
@@ -455,7 +420,7 @@
assertRunOnServiceThread();
int logicalAddress = message.getSource();
int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
- HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress);
+ HdmiDeviceInfo info = mService.getHdmiCecNetwork().getCecDeviceInfo(logicalAddress);
if (info == null) {
if (!handleNewDeviceAtTheTailOfActivePath(physicalAddress)) {
HdmiLogger.debug("Device info %X not found; buffering the command", logicalAddress);
@@ -463,7 +428,8 @@
}
} else if (isInputReady(info.getId())
|| info.getDeviceType() == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) {
- updateDevicePowerStatus(logicalAddress, HdmiControlManager.POWER_STATUS_ON);
+ mService.getHdmiCecNetwork().updateDevicePowerStatus(logicalAddress,
+ HdmiControlManager.POWER_STATUS_ON);
ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
ActiveSourceHandler.create(this, null).process(activeSource, info.getDeviceType());
} else {
@@ -490,7 +456,8 @@
if (portId != Constants.INVALID_PORT_ID) {
// TODO: Do this only if TV is not showing multiview like PIP/PAP.
- HdmiDeviceInfo inactiveSource = getCecDeviceInfo(message.getSource());
+ HdmiDeviceInfo inactiveSource = mService.getHdmiCecNetwork().getCecDeviceInfo(
+ message.getSource());
if (inactiveSource == null) {
return true;
}
@@ -546,42 +513,20 @@
}
@Override
- @ServiceThreadOnly
protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
- assertRunOnServiceThread();
+ super.handleReportPhysicalAddress(message);
int path = HdmiUtils.twoBytesToInt(message.getParams());
int address = message.getSource();
int type = message.getParams()[2];
- if (updateCecSwitchInfo(address, type, path)) return true;
-
- // Ignore if [Device Discovery Action] is going on.
- if (hasAction(DeviceDiscoveryAction.class)) {
- Slog.i(TAG, "Ignored while Device Discovery Action is in progress: " + message);
- return true;
- }
-
- if (!isInDeviceList(address, path)) {
+ if (!mService.getHdmiCecNetwork().isInDeviceList(address, path)) {
handleNewDeviceAtTheTailOfActivePath(path);
}
-
- // Add the device ahead with default information to handle <Active Source>
- // promptly, rather than waiting till the new device action is finished.
- HdmiDeviceInfo deviceInfo = new HdmiDeviceInfo(address, path, getPortId(path), type,
- Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(address));
- addCecDevice(deviceInfo);
startNewDeviceAction(ActiveSource.of(address, path), type);
return true;
}
@Override
- protected boolean handleReportPowerStatus(HdmiCecMessage command) {
- int newStatus = command.getParams()[0] & 0xFF;
- updateDevicePowerStatus(command.getSource(), newStatus);
- return true;
- }
-
- @Override
protected boolean handleTimerStatus(HdmiCecMessage message) {
// Do nothing.
return true;
@@ -593,19 +538,6 @@
return true;
}
- boolean updateCecSwitchInfo(int address, int type, int path) {
- if (address == Constants.ADDR_UNREGISTERED
- && type == HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH) {
- mCecSwitches.add(path);
- updateSafeDeviceInfoList();
- return true; // Pure switch does not need further processing. Return here.
- }
- if (type == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) {
- mCecSwitches.add(path);
- }
- return false;
- }
-
void startNewDeviceAction(ActiveSource activeSource, int deviceType) {
for (NewDeviceAction action : getActions(NewDeviceAction.class)) {
// If there is new device action which has the same logical address and path
@@ -719,35 +651,6 @@
return handleTextViewOn(message);
}
- @Override
- @ServiceThreadOnly
- protected boolean handleSetOsdName(HdmiCecMessage message) {
- int source = message.getSource();
- HdmiDeviceInfo deviceInfo = getCecDeviceInfo(source);
- // If the device is not in device list, ignore it.
- if (deviceInfo == null) {
- Slog.e(TAG, "No source device info for <Set Osd Name>." + message);
- return true;
- }
- String osdName = null;
- try {
- osdName = new String(message.getParams(), "US-ASCII");
- } catch (UnsupportedEncodingException e) {
- Slog.e(TAG, "Invalid <Set Osd Name> request:" + message, e);
- return true;
- }
-
- if (deviceInfo.getDisplayName().equals(osdName)) {
- Slog.i(TAG, "Ignore incoming <Set Osd Name> having same osd name:" + message);
- return true;
- }
-
- addCecDevice(new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
- deviceInfo.getPhysicalAddress(), deviceInfo.getPortId(),
- deviceInfo.getDeviceType(), deviceInfo.getVendorId(), osdName));
- return true;
- }
-
@ServiceThreadOnly
private void launchDeviceDiscovery() {
assertRunOnServiceThread();
@@ -757,14 +660,14 @@
@Override
public void onDeviceDiscoveryDone(List<HdmiDeviceInfo> deviceInfos) {
for (HdmiDeviceInfo info : deviceInfos) {
- addCecDevice(info);
+ mService.getHdmiCecNetwork().addCecDevice(info);
}
// Since we removed all devices when it's start and
// device discovery action does not poll local devices,
// we should put device info of local device manually here
for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) {
- addCecDevice(device.getDeviceInfo());
+ mService.getHdmiCecNetwork().addCecDevice(device.getDeviceInfo());
}
mSelectRequestBuffer.process();
@@ -798,11 +701,7 @@
@ServiceThreadOnly
private void clearDeviceInfoList() {
assertRunOnServiceThread();
- for (HdmiDeviceInfo info : mSafeExternalInputs) {
- invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
- }
- mDeviceInfos.clear();
- updateSafeDeviceInfoList();
+ mService.getHdmiCecNetwork().clearDeviceList();
}
@ServiceThreadOnly
@@ -1224,170 +1123,10 @@
&& getAvrDeviceInfo() != null;
}
- /**
- * Add a new {@link HdmiDeviceInfo}. It returns old device info which has the same
- * logical address as new device info's.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- *
- * @param deviceInfo a new {@link HdmiDeviceInfo} to be added.
- * @return {@code null} if it is new device. Otherwise, returns old {@HdmiDeviceInfo}
- * that has the same logical address as new one has.
- */
- @ServiceThreadOnly
- private HdmiDeviceInfo addDeviceInfo(HdmiDeviceInfo deviceInfo) {
- assertRunOnServiceThread();
- HdmiDeviceInfo oldDeviceInfo = getCecDeviceInfo(deviceInfo.getLogicalAddress());
- if (oldDeviceInfo != null) {
- removeDeviceInfo(deviceInfo.getId());
- }
- mDeviceInfos.append(deviceInfo.getId(), deviceInfo);
- updateSafeDeviceInfoList();
- return oldDeviceInfo;
- }
-
- /**
- * Remove a device info corresponding to the given {@code logicalAddress}.
- * It returns removed {@link HdmiDeviceInfo} if exists.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- *
- * @param id id of device to be removed
- * @return removed {@link HdmiDeviceInfo} it exists. Otherwise, returns {@code null}
- */
- @ServiceThreadOnly
- private HdmiDeviceInfo removeDeviceInfo(int id) {
- assertRunOnServiceThread();
- HdmiDeviceInfo deviceInfo = mDeviceInfos.get(id);
- if (deviceInfo != null) {
- mDeviceInfos.remove(id);
- }
- updateSafeDeviceInfoList();
- return deviceInfo;
- }
-
- /**
- * Return a list of all {@link HdmiDeviceInfo}.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- * This is not thread-safe. For thread safety, call {@link #getSafeExternalInputsLocked} which
- * does not include local device.
- */
- @ServiceThreadOnly
- List<HdmiDeviceInfo> getDeviceInfoList(boolean includeLocalDevice) {
- assertRunOnServiceThread();
- if (includeLocalDevice) {
- return HdmiUtils.sparseArrayToList(mDeviceInfos);
- } else {
- ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
- for (int i = 0; i < mDeviceInfos.size(); ++i) {
- HdmiDeviceInfo info = mDeviceInfos.valueAt(i);
- if (!isLocalDeviceAddress(info.getLogicalAddress())) {
- infoList.add(info);
- }
- }
- return infoList;
- }
- }
-
- /**
- * Return external input devices.
- */
- @GuardedBy("mLock")
- List<HdmiDeviceInfo> getSafeExternalInputsLocked() {
- return mSafeExternalInputs;
- }
-
- @ServiceThreadOnly
- private void updateSafeDeviceInfoList() {
- assertRunOnServiceThread();
- List<HdmiDeviceInfo> copiedDevices = HdmiUtils.sparseArrayToList(mDeviceInfos);
- List<HdmiDeviceInfo> externalInputs = getInputDevices();
- synchronized (mLock) {
- mSafeAllDeviceInfos = copiedDevices;
- mSafeExternalInputs = externalInputs;
- }
- }
-
- /**
- * Return a list of external cec input (source) devices.
- *
- * <p>Note that this effectively excludes non-source devices like system audio,
- * secondary TV.
- */
- private List<HdmiDeviceInfo> getInputDevices() {
- ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
- for (int i = 0; i < mDeviceInfos.size(); ++i) {
- HdmiDeviceInfo info = mDeviceInfos.valueAt(i);
- if (isLocalDeviceAddress(info.getLogicalAddress())) {
- continue;
- }
- if (info.isSourceType() && !hideDevicesBehindLegacySwitch(info)) {
- infoList.add(info);
- }
- }
- return infoList;
- }
-
- // Check if we are hiding CEC devices connected to a legacy (non-CEC) switch.
- // Returns true if the policy is set to true, and the device to check does not have
- // a parent CEC device (which should be the CEC-enabled switch) in the list.
- private boolean hideDevicesBehindLegacySwitch(HdmiDeviceInfo info) {
- return HdmiConfig.HIDE_DEVICES_BEHIND_LEGACY_SWITCH
- && !isConnectedToCecSwitch(info.getPhysicalAddress(), mCecSwitches);
- }
-
- private static boolean isConnectedToCecSwitch(int path, Collection<Integer> switches) {
- for (int switchPath : switches) {
- if (isParentPath(switchPath, path)) {
- return true;
- }
- }
- return false;
- }
-
- private static boolean isParentPath(int parentPath, int childPath) {
- // (A000, AB00) (AB00, ABC0), (ABC0, ABCD)
- // If child's last non-zero nibble is removed, the result equals to the parent.
- for (int i = 0; i <= 12; i += 4) {
- int nibble = (childPath >> i) & 0xF;
- if (nibble != 0) {
- int parentNibble = (parentPath >> i) & 0xF;
- return parentNibble == 0 && (childPath >> i+4) == (parentPath >> i+4);
- }
- }
- return false;
- }
-
- private void invokeDeviceEventListener(HdmiDeviceInfo info, int status) {
- if (!hideDevicesBehindLegacySwitch(info)) {
- mService.invokeDeviceEventListeners(info, status);
- }
- }
-
- private boolean isLocalDeviceAddress(int address) {
- return mLocalDeviceAddresses.contains(address);
- }
-
@ServiceThreadOnly
HdmiDeviceInfo getAvrDeviceInfo() {
assertRunOnServiceThread();
- return getCecDeviceInfo(Constants.ADDR_AUDIO_SYSTEM);
- }
-
- /**
- * Return a {@link HdmiDeviceInfo} corresponding to the given {@code logicalAddress}.
- *
- * This is not thread-safe. For thread safety, call {@link #getSafeCecDeviceInfo(int)}.
- *
- * @param logicalAddress logical address of the device to be retrieved
- * @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
- * Returns null if no logical address matched
- */
- @ServiceThreadOnly
- HdmiDeviceInfo getCecDeviceInfo(int logicalAddress) {
- assertRunOnServiceThread();
- return mDeviceInfos.get(HdmiDeviceInfo.idForCecDevice(logicalAddress));
+ return mService.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_AUDIO_SYSTEM);
}
boolean hasSystemAudioDevice() {
@@ -1395,74 +1134,9 @@
}
HdmiDeviceInfo getSafeAvrDeviceInfo() {
- return getSafeCecDeviceInfo(Constants.ADDR_AUDIO_SYSTEM);
+ return mService.getHdmiCecNetwork().getSafeCecDeviceInfo(Constants.ADDR_AUDIO_SYSTEM);
}
- /**
- * Thread safe version of {@link #getCecDeviceInfo(int)}.
- *
- * @param logicalAddress logical address to be retrieved
- * @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
- * Returns null if no logical address matched
- */
- HdmiDeviceInfo getSafeCecDeviceInfo(int logicalAddress) {
- synchronized (mLock) {
- for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
- if (info.isCecDevice() && info.getLogicalAddress() == logicalAddress) {
- return info;
- }
- }
- return null;
- }
- }
-
- @GuardedBy("mLock")
- List<HdmiDeviceInfo> getSafeCecDevicesLocked() {
- ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
- for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
- if (isLocalDeviceAddress(info.getLogicalAddress())) {
- continue;
- }
- infoList.add(info);
- }
- return infoList;
- }
-
- /**
- * Called when a device is newly added or a new device is detected or
- * existing device is updated.
- *
- * @param info device info of a new device.
- */
- @ServiceThreadOnly
- final void addCecDevice(HdmiDeviceInfo info) {
- assertRunOnServiceThread();
- HdmiDeviceInfo old = addDeviceInfo(info);
- if (info.getLogicalAddress() == mAddress) {
- // The addition of TV device itself should not be notified.
- return;
- }
- if (old == null) {
- invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
- } else if (!old.equals(info)) {
- invokeDeviceEventListener(old, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
- invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
- }
- }
-
- /**
- * Called when a device is removed or removal of device is detected.
- *
- * @param address a logical address of a device to be removed
- */
- @ServiceThreadOnly
- final void removeCecDevice(int address) {
- assertRunOnServiceThread();
- HdmiDeviceInfo info = removeDeviceInfo(HdmiDeviceInfo.idForCecDevice(address));
-
- mCecMessageCache.flushMessagesFrom(address);
- invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
- }
@ServiceThreadOnly
void handleRemoveActiveRoutingPath(int path) {
@@ -1501,72 +1175,10 @@
}
}
- /**
- * Returns the {@link HdmiDeviceInfo} instance whose physical address matches
- * the given routing path. CEC devices use routing path for its physical address to
- * describe the hierarchy of the devices in the network.
- *
- * @param path routing path or physical address
- * @return {@link HdmiDeviceInfo} if the matched info is found; otherwise null
- */
- @ServiceThreadOnly
- final HdmiDeviceInfo getDeviceInfoByPath(int path) {
- assertRunOnServiceThread();
- for (HdmiDeviceInfo info : getDeviceInfoList(false)) {
- if (info.getPhysicalAddress() == path) {
- return info;
- }
- }
- return null;
- }
-
- /**
- * Returns the {@link HdmiDeviceInfo} instance whose physical address matches
- * the given routing path. This is the version accessible safely from threads
- * other than service thread.
- *
- * @param path routing path or physical address
- * @return {@link HdmiDeviceInfo} if the matched info is found; otherwise null
- */
- HdmiDeviceInfo getSafeDeviceInfoByPath(int path) {
- synchronized (mLock) {
- for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
- if (info.getPhysicalAddress() == path) {
- return info;
- }
- }
- return null;
- }
- }
-
- /**
- * Whether a device of the specified physical address and logical address exists
- * in a device info list. However, both are minimal condition and it could
- * be different device from the original one.
- *
- * @param logicalAddress logical address of a device to be searched
- * @param physicalAddress physical address of a device to be searched
- * @return true if exist; otherwise false
- */
- @ServiceThreadOnly
- boolean isInDeviceList(int logicalAddress, int physicalAddress) {
- assertRunOnServiceThread();
- HdmiDeviceInfo device = getCecDeviceInfo(logicalAddress);
- if (device == null) {
- return false;
- }
- return device.getPhysicalAddress() == physicalAddress;
- }
-
@Override
@ServiceThreadOnly
void onHotplug(int portId, boolean connected) {
assertRunOnServiceThread();
-
- if (!connected) {
- removeCecSwitches(portId);
- }
-
// Turning System Audio Mode off when the AVR is unlugged or standby.
// When the device is not unplugged but reawaken from standby, we check if the System
// Audio Control Feature is enabled or not then decide if turning SAM on/off accordingly.
@@ -1588,16 +1200,6 @@
}
}
- private void removeCecSwitches(int portId) {
- Iterator<Integer> it = mCecSwitches.iterator();
- while (!it.hasNext()) {
- int path = it.next();
- if (pathToPortId(path) == portId) {
- it.remove();
- }
- }
- }
-
@Override
@ServiceThreadOnly
void setAutoDeviceOff(boolean enabled) {
@@ -1765,7 +1367,7 @@
}
private boolean checkRecorder(int recorderAddress) {
- HdmiDeviceInfo device = getCecDeviceInfo(recorderAddress);
+ HdmiDeviceInfo device = mService.getHdmiCecNetwork().getCecDeviceInfo(recorderAddress);
return (device != null)
&& (HdmiUtils.getTypeFromAddress(recorderAddress)
== HdmiDeviceInfo.DEVICE_RECORDER);
@@ -1871,24 +1473,6 @@
});
}
- void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) {
- HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress);
- if (info == null) {
- Slog.w(TAG, "Can not update power status of non-existing device:" + logicalAddress);
- return;
- }
-
- if (info.getDevicePowerStatus() == newPowerStatus) {
- return;
- }
-
- HdmiDeviceInfo newInfo = HdmiUtils.cloneHdmiDeviceInfo(info, newPowerStatus);
- // addDeviceInfo replaces old device info with new one if exists.
- addDeviceInfo(newInfo);
-
- invokeDeviceEventListener(newInfo, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
- }
-
@Override
protected boolean handleMenuStatus(HdmiCecMessage message) {
// Do nothing and just return true not to prevent from responding <Feature Abort>.
@@ -1897,7 +1481,7 @@
@Override
protected void sendStandby(int deviceId) {
- HdmiDeviceInfo targetDevice = mDeviceInfos.get(deviceId);
+ HdmiDeviceInfo targetDevice = mService.getHdmiCecNetwork().getDeviceInfo(deviceId);
if (targetDevice == null) {
return;
}
@@ -1934,11 +1518,5 @@
pw.println("mAutoWakeup: " + mAutoWakeup);
pw.println("mSkipRoutingControl: " + mSkipRoutingControl);
pw.println("mPrevPortId: " + mPrevPortId);
- pw.println("CEC devices:");
- pw.increaseIndent();
- for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
- pw.println(info);
- }
- pw.decreaseIndent();
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index 28bd97e..d4593af 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -128,7 +128,7 @@
FixedLengthValidator oneByteValidator = new FixedLengthValidator(1);
addValidationInfo(Constants.MESSAGE_CEC_VERSION, oneByteValidator, DEST_DIRECT);
addValidationInfo(Constants.MESSAGE_SET_MENU_LANGUAGE,
- new FixedLengthValidator(3), DEST_BROADCAST);
+ new AsciiValidator(3), DEST_BROADCAST);
// TODO: Handle messages for the Deck Control.
@@ -148,8 +148,8 @@
maxLengthValidator, DEST_ALL | SRC_UNREGISTERED);
// Messages for the OSD.
- addValidationInfo(Constants.MESSAGE_SET_OSD_STRING, maxLengthValidator, DEST_DIRECT);
- addValidationInfo(Constants.MESSAGE_SET_OSD_NAME, maxLengthValidator, DEST_DIRECT);
+ addValidationInfo(Constants.MESSAGE_SET_OSD_STRING, new OsdStringValidator(), DEST_DIRECT);
+ addValidationInfo(Constants.MESSAGE_SET_OSD_NAME, new AsciiValidator(1, 14), DEST_DIRECT);
// Messages for the Device Menu Control.
addValidationInfo(Constants.MESSAGE_MENU_REQUEST, oneByteValidator, DEST_DIRECT);
@@ -299,6 +299,37 @@
return (value >= min && value <= max);
}
+ /**
+ * Check if the given value is a valid Display Control. A valid value is one which falls within
+ * the range description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
+ *
+ * @param value Display Control
+ * @return true if the Display Control is valid
+ */
+ private boolean isValidDisplayControl(int value) {
+ value = value & 0xFF;
+ return (value == 0x00 || value == 0x40 || value == 0x80 || value == 0xC0);
+ }
+
+ /**
+ * Check if the given params has valid ASCII characters.
+ * A valid ASCII character is a printable character. It should fall within range description
+ * defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
+ *
+ * @param params parameter consisting of string
+ * @param offset Start offset of string
+ * @param maxLength Maximum length of string to be evaluated
+ * @return true if the given type is valid
+ */
+ private boolean isValidAsciiString(byte[] params, int offset, int maxLength) {
+ for (int i = offset; i < params.length && i < maxLength; i++) {
+ if (!isWithinRange(params[i], 0x20, 0x7E)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private class PhysicalAddressValidator implements ParameterValidator {
@Override
public int isValid(byte[] params) {
@@ -359,4 +390,55 @@
|| params[0] == 0x1F);
}
}
+
+ /**
+ * Check if the given parameters represents printable characters.
+ * A valid parameter should lie within the range description of ASCII defined in CEC 1.4
+ * Specification : Operand Descriptions (Section 17)
+ */
+ private class AsciiValidator implements ParameterValidator {
+ private final int mMinLength;
+ private final int mMaxLength;
+
+ AsciiValidator(int length) {
+ mMinLength = length;
+ mMaxLength = length;
+ }
+
+ AsciiValidator(int minLength, int maxLength) {
+ mMinLength = minLength;
+ mMaxLength = maxLength;
+ }
+
+ @Override
+ public int isValid(byte[] params) {
+ // If the length is longer than expected, we assume it's OK since the parameter can be
+ // extended in the future version.
+ if (params.length < mMinLength) {
+ return ERROR_PARAMETER_SHORT;
+ }
+ return toErrorCode(isValidAsciiString(params, 0, mMaxLength));
+ }
+ }
+
+ /**
+ * Check if the given parameters is valid OSD String.
+ * A valid parameter should lie within the range description of ASCII defined in CEC 1.4
+ * Specification : Operand Descriptions (Section 17)
+ */
+ private class OsdStringValidator implements ParameterValidator {
+ @Override
+ public int isValid(byte[] params) {
+ // If the length is longer than expected, we assume it's OK since the parameter can be
+ // extended in the future version.
+ if (params.length < 2) {
+ return ERROR_PARAMETER_SHORT;
+ }
+ return toErrorCode(
+ // Display Control
+ isValidDisplayControl(params[0])
+ // OSD String
+ && isValidAsciiString(params, 1, 14));
+ }
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
new file mode 100644
index 0000000..5d75a63
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
@@ -0,0 +1,846 @@
+/*
+ * 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.hdmi;
+
+import static com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
+
+import android.annotation.Nullable;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiPortInfo;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.UnsupportedEncodingException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+
+/**
+ * Holds information about the current state of the HDMI CEC network. It is the sole source of
+ * truth for device information in the CEC network.
+ *
+ * This information includes:
+ * - All local devices
+ * - All HDMI ports, their capabilities and status
+ * - All devices connected to the CEC bus
+ *
+ * This class receives all incoming CEC messages and passively listens to device updates to fill
+ * out the above information.
+ * This class should not take any active action in sending CEC messages.
+ *
+ * Note that the information cached in this class is not guaranteed to be up-to-date, especially OSD
+ * names, power states can be outdated.
+ */
+class HdmiCecNetwork {
+ private static final String TAG = "HdmiCecNetwork";
+
+ protected final Object mLock;
+ private final HdmiControlService mHdmiControlService;
+ private final HdmiCecController mHdmiCecController;
+ private final HdmiMhlControllerStub mHdmiMhlController;
+ private final Handler mHandler;
+ // Stores the local CEC devices in the system. Device type is used for key.
+ private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
+
+ // Map-like container of all cec devices including local ones.
+ // device id is used as key of container.
+ // This is not thread-safe. For external purpose use mSafeDeviceInfos.
+ private final SparseArray<HdmiDeviceInfo> mDeviceInfos = new SparseArray<>();
+ // Set of physical addresses of CEC switches on the CEC bus. Managed independently from
+ // other CEC devices since they might not have logical address.
+ private final ArraySet<Integer> mCecSwitches = new ArraySet<>();
+ // Copy of mDeviceInfos to guarantee thread-safety.
+ @GuardedBy("mLock")
+ private List<HdmiDeviceInfo> mSafeAllDeviceInfos = Collections.emptyList();
+ // All external cec input(source) devices. Does not include system audio device.
+ @GuardedBy("mLock")
+ private List<HdmiDeviceInfo> mSafeExternalInputs = Collections.emptyList();
+ // HDMI port information. Stored in the unmodifiable list to keep the static information
+ // from being modified.
+ @GuardedBy("mLock")
+ private List<HdmiPortInfo> mPortInfo = Collections.emptyList();
+
+ // Map from path(physical address) to port ID.
+ private UnmodifiableSparseIntArray mPortIdMap;
+
+ // Map from port ID to HdmiPortInfo.
+ private UnmodifiableSparseArray<HdmiPortInfo> mPortInfoMap;
+
+ // Map from port ID to HdmiDeviceInfo.
+ private UnmodifiableSparseArray<HdmiDeviceInfo> mPortDeviceMap;
+
+ HdmiCecNetwork(HdmiControlService hdmiControlService,
+ HdmiCecController hdmiCecController,
+ HdmiMhlControllerStub hdmiMhlController) {
+ mHdmiControlService = hdmiControlService;
+ mHdmiCecController = hdmiCecController;
+ mHdmiMhlController = hdmiMhlController;
+ mHandler = new Handler(mHdmiControlService.getServiceLooper());
+ mLock = mHdmiControlService.getServiceLock();
+ }
+
+ private static boolean isConnectedToCecSwitch(int path, Collection<Integer> switches) {
+ for (int switchPath : switches) {
+ if (isParentPath(switchPath, path)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean isParentPath(int parentPath, int childPath) {
+ // (A000, AB00) (AB00, ABC0), (ABC0, ABCD)
+ // If child's last non-zero nibble is removed, the result equals to the parent.
+ for (int i = 0; i <= 12; i += 4) {
+ int nibble = (childPath >> i) & 0xF;
+ if (nibble != 0) {
+ int parentNibble = (parentPath >> i) & 0xF;
+ return parentNibble == 0 && (childPath >> i + 4) == (parentPath >> i + 4);
+ }
+ }
+ return false;
+ }
+
+ public void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
+ mLocalDevices.put(deviceType, device);
+ }
+
+ /**
+ * Return the locally hosted logical device of a given type.
+ *
+ * @param deviceType logical device type
+ * @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
+ * otherwise null.
+ */
+ HdmiCecLocalDevice getLocalDevice(int deviceType) {
+ return mLocalDevices.get(deviceType);
+ }
+
+ /**
+ * Return a list of all {@link HdmiCecLocalDevice}s.
+ *
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ */
+ @ServiceThreadOnly
+ List<HdmiCecLocalDevice> getLocalDeviceList() {
+ assertRunOnServiceThread();
+ return HdmiUtils.sparseArrayToList(mLocalDevices);
+ }
+
+ @ServiceThreadOnly
+ boolean isAllocatedLocalDeviceAddress(int address) {
+ assertRunOnServiceThread();
+ for (int i = 0; i < mLocalDevices.size(); ++i) {
+ if (mLocalDevices.valueAt(i).isAddressOf(address)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Clear all logical addresses registered in the device.
+ *
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ */
+ @ServiceThreadOnly
+ void clearLogicalAddress() {
+ assertRunOnServiceThread();
+ for (int i = 0; i < mLocalDevices.size(); ++i) {
+ mLocalDevices.valueAt(i).clearAddress();
+ }
+ }
+
+ @ServiceThreadOnly
+ void clearLocalDevices() {
+ assertRunOnServiceThread();
+ mLocalDevices.clear();
+ }
+
+ public HdmiDeviceInfo getDeviceInfo(int id) {
+ return mDeviceInfos.get(id);
+ }
+
+ /**
+ * Add a new {@link HdmiDeviceInfo}. It returns old device info which has the same
+ * logical address as new device info's.
+ *
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ *
+ * @param deviceInfo a new {@link HdmiDeviceInfo} to be added.
+ * @return {@code null} if it is new device. Otherwise, returns old {@HdmiDeviceInfo}
+ * that has the same logical address as new one has.
+ */
+ @ServiceThreadOnly
+ private HdmiDeviceInfo addDeviceInfo(HdmiDeviceInfo deviceInfo) {
+ assertRunOnServiceThread();
+ HdmiDeviceInfo oldDeviceInfo = getCecDeviceInfo(deviceInfo.getLogicalAddress());
+ mHdmiControlService.checkLogicalAddressConflictAndReallocate(
+ deviceInfo.getLogicalAddress(), deviceInfo.getPhysicalAddress());
+ if (oldDeviceInfo != null) {
+ removeDeviceInfo(deviceInfo.getId());
+ }
+ mDeviceInfos.append(deviceInfo.getId(), deviceInfo);
+ updateSafeDeviceInfoList();
+ return oldDeviceInfo;
+ }
+
+ /**
+ * Remove a device info corresponding to the given {@code logicalAddress}.
+ * It returns removed {@link HdmiDeviceInfo} if exists.
+ *
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ *
+ * @param id id of device to be removed
+ * @return removed {@link HdmiDeviceInfo} it exists. Otherwise, returns {@code null}
+ */
+ @ServiceThreadOnly
+ private HdmiDeviceInfo removeDeviceInfo(int id) {
+ assertRunOnServiceThread();
+ HdmiDeviceInfo deviceInfo = mDeviceInfos.get(id);
+ if (deviceInfo != null) {
+ mDeviceInfos.remove(id);
+ }
+ updateSafeDeviceInfoList();
+ return deviceInfo;
+ }
+
+ /**
+ * Return a {@link HdmiDeviceInfo} corresponding to the given {@code logicalAddress}.
+ *
+ * This is not thread-safe. For thread safety, call {@link #getSafeCecDeviceInfo(int)}.
+ *
+ * @param logicalAddress logical address of the device to be retrieved
+ * @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
+ * Returns null if no logical address matched
+ */
+ @ServiceThreadOnly
+ @Nullable
+ HdmiDeviceInfo getCecDeviceInfo(int logicalAddress) {
+ assertRunOnServiceThread();
+ return mDeviceInfos.get(HdmiDeviceInfo.idForCecDevice(logicalAddress));
+ }
+
+ /**
+ * Called when a device is newly added or a new device is detected or
+ * existing device is updated.
+ *
+ * @param info device info of a new device.
+ */
+ @ServiceThreadOnly
+ final void addCecDevice(HdmiDeviceInfo info) {
+ assertRunOnServiceThread();
+ HdmiDeviceInfo old = addDeviceInfo(info);
+ if (isLocalDeviceAddress(info.getLogicalAddress())) {
+ // The addition of a local device should not notify listeners
+ return;
+ }
+ if (old == null) {
+ invokeDeviceEventListener(info,
+ HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
+ } else if (!old.equals(info)) {
+ invokeDeviceEventListener(old,
+ HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
+ invokeDeviceEventListener(info,
+ HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
+ }
+ }
+
+ private void invokeDeviceEventListener(HdmiDeviceInfo info, int event) {
+ if (!hideDevicesBehindLegacySwitch(info)) {
+ mHdmiControlService.invokeDeviceEventListeners(info, event);
+ }
+ }
+
+ /**
+ * Called when a device is updated.
+ *
+ * @param info device info of the updating device.
+ */
+ @ServiceThreadOnly
+ final void updateCecDevice(HdmiDeviceInfo info) {
+ assertRunOnServiceThread();
+ HdmiDeviceInfo old = addDeviceInfo(info);
+
+ if (old == null) {
+ invokeDeviceEventListener(info,
+ HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
+ } else if (!old.equals(info)) {
+ invokeDeviceEventListener(info,
+ HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
+ }
+ }
+
+ @ServiceThreadOnly
+ private void updateSafeDeviceInfoList() {
+ assertRunOnServiceThread();
+ List<HdmiDeviceInfo> copiedDevices = HdmiUtils.sparseArrayToList(mDeviceInfos);
+ List<HdmiDeviceInfo> externalInputs = getInputDevices();
+ mSafeAllDeviceInfos = copiedDevices;
+ mSafeExternalInputs = externalInputs;
+ }
+
+ /**
+ * Return a list of all {@link HdmiDeviceInfo}.
+ *
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ * This is not thread-safe. For thread safety, call {@link #getSafeExternalInputsLocked} which
+ * does not include local device.
+ */
+ @ServiceThreadOnly
+ List<HdmiDeviceInfo> getDeviceInfoList(boolean includeLocalDevice) {
+ assertRunOnServiceThread();
+ if (includeLocalDevice) {
+ return HdmiUtils.sparseArrayToList(mDeviceInfos);
+ } else {
+ ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
+ for (int i = 0; i < mDeviceInfos.size(); ++i) {
+ HdmiDeviceInfo info = mDeviceInfos.valueAt(i);
+ if (!isLocalDeviceAddress(info.getLogicalAddress())) {
+ infoList.add(info);
+ }
+ }
+ return infoList;
+ }
+ }
+
+ /**
+ * Return external input devices.
+ */
+ @GuardedBy("mLock")
+ List<HdmiDeviceInfo> getSafeExternalInputsLocked() {
+ return mSafeExternalInputs;
+ }
+
+ /**
+ * Return a list of external cec input (source) devices.
+ *
+ * <p>Note that this effectively excludes non-source devices like system audio,
+ * secondary TV.
+ */
+ private List<HdmiDeviceInfo> getInputDevices() {
+ ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
+ for (int i = 0; i < mDeviceInfos.size(); ++i) {
+ HdmiDeviceInfo info = mDeviceInfos.valueAt(i);
+ if (isLocalDeviceAddress(info.getLogicalAddress())) {
+ continue;
+ }
+ if (info.isSourceType() && !hideDevicesBehindLegacySwitch(info)) {
+ infoList.add(info);
+ }
+ }
+ return infoList;
+ }
+
+ // Check if we are hiding CEC devices connected to a legacy (non-CEC) switch.
+ // This only applies to TV devices.
+ // Returns true if the policy is set to true, and the device to check does not have
+ // a parent CEC device (which should be the CEC-enabled switch) in the list.
+ private boolean hideDevicesBehindLegacySwitch(HdmiDeviceInfo info) {
+ return isLocalDeviceAddress(Constants.ADDR_TV)
+ && HdmiConfig.HIDE_DEVICES_BEHIND_LEGACY_SWITCH
+ && !isConnectedToCecSwitch(info.getPhysicalAddress(), getCecSwitches());
+ }
+
+ /**
+ * Called when a device is removed or removal of device is detected.
+ *
+ * @param address a logical address of a device to be removed
+ */
+ @ServiceThreadOnly
+ final void removeCecDevice(HdmiCecLocalDevice localDevice, int address) {
+ assertRunOnServiceThread();
+ HdmiDeviceInfo info = removeDeviceInfo(HdmiDeviceInfo.idForCecDevice(address));
+
+ localDevice.mCecMessageCache.flushMessagesFrom(address);
+ invokeDeviceEventListener(info,
+ HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
+ }
+
+ public void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) {
+ HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress);
+ if (info == null) {
+ Slog.w(TAG, "Can not update power status of non-existing device:" + logicalAddress);
+ return;
+ }
+
+ if (info.getDevicePowerStatus() == newPowerStatus) {
+ return;
+ }
+
+ updateCecDevice(HdmiUtils.cloneHdmiDeviceInfo(info, newPowerStatus));
+ }
+
+ /**
+ * Whether a device of the specified physical address is connected to ARC enabled port.
+ */
+ boolean isConnectedToArcPort(int physicalAddress) {
+ int portId = physicalAddressToPortId(physicalAddress);
+ if (portId != Constants.INVALID_PORT_ID) {
+ return mPortInfoMap.get(portId).isArcSupported();
+ }
+ return false;
+ }
+
+
+ // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
+ // keep them in one place.
+ @ServiceThreadOnly
+ @VisibleForTesting
+ public void initPortInfo() {
+ assertRunOnServiceThread();
+ HdmiPortInfo[] cecPortInfo = null;
+ // CEC HAL provides majority of the info while MHL does only MHL support flag for
+ // each port. Return empty array if CEC HAL didn't provide the info.
+ if (mHdmiCecController != null) {
+ cecPortInfo = mHdmiCecController.getPortInfos();
+ }
+ if (cecPortInfo == null) {
+ return;
+ }
+
+ SparseArray<HdmiPortInfo> portInfoMap = new SparseArray<>();
+ SparseIntArray portIdMap = new SparseIntArray();
+ SparseArray<HdmiDeviceInfo> portDeviceMap = new SparseArray<>();
+ for (HdmiPortInfo info : cecPortInfo) {
+ portIdMap.put(info.getAddress(), info.getId());
+ portInfoMap.put(info.getId(), info);
+ portDeviceMap.put(info.getId(), new HdmiDeviceInfo(info.getAddress(), info.getId()));
+ }
+ mPortIdMap = new UnmodifiableSparseIntArray(portIdMap);
+ mPortInfoMap = new UnmodifiableSparseArray<>(portInfoMap);
+ mPortDeviceMap = new UnmodifiableSparseArray<>(portDeviceMap);
+
+ if (mHdmiMhlController == null) {
+ return;
+ }
+ HdmiPortInfo[] mhlPortInfo = mHdmiMhlController.getPortInfos();
+ ArraySet<Integer> mhlSupportedPorts = new ArraySet<Integer>(mhlPortInfo.length);
+ for (HdmiPortInfo info : mhlPortInfo) {
+ if (info.isMhlSupported()) {
+ mhlSupportedPorts.add(info.getId());
+ }
+ }
+
+ // Build HDMI port info list with CEC port info plus MHL supported flag. We can just use
+ // cec port info if we do not have have port that supports MHL.
+ if (mhlSupportedPorts.isEmpty()) {
+ setPortInfo(Collections.unmodifiableList(Arrays.asList(cecPortInfo)));
+ return;
+ }
+ ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
+ for (HdmiPortInfo info : cecPortInfo) {
+ if (mhlSupportedPorts.contains(info.getId())) {
+ result.add(new HdmiPortInfo(info.getId(), info.getType(), info.getAddress(),
+ info.isCecSupported(), true, info.isArcSupported()));
+ } else {
+ result.add(info);
+ }
+ }
+ setPortInfo(Collections.unmodifiableList(result));
+ }
+
+ HdmiDeviceInfo getDeviceForPortId(int portId) {
+ return mPortDeviceMap.get(portId, HdmiDeviceInfo.INACTIVE_DEVICE);
+ }
+
+ /**
+ * Whether a device of the specified physical address and logical address exists
+ * in a device info list. However, both are minimal condition and it could
+ * be different device from the original one.
+ *
+ * @param logicalAddress logical address of a device to be searched
+ * @param physicalAddress physical address of a device to be searched
+ * @return true if exist; otherwise false
+ */
+ @ServiceThreadOnly
+ boolean isInDeviceList(int logicalAddress, int physicalAddress) {
+ assertRunOnServiceThread();
+ HdmiDeviceInfo device = getCecDeviceInfo(logicalAddress);
+ if (device == null) {
+ return false;
+ }
+ return device.getPhysicalAddress() == physicalAddress;
+ }
+
+ /**
+ * Passively listen to incoming CEC messages.
+ *
+ * This shall not result in any CEC messages being sent.
+ */
+ @ServiceThreadOnly
+ public void handleCecMessage(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ // Add device by logical address if it's not already known
+ int sourceAddress = message.getSource();
+ if (getCecDeviceInfo(sourceAddress) == null) {
+ HdmiDeviceInfo newDevice = new HdmiDeviceInfo(sourceAddress,
+ HdmiDeviceInfo.PATH_INVALID, HdmiDeviceInfo.PORT_INVALID,
+ HdmiDeviceInfo.DEVICE_RESERVED, Constants.UNKNOWN_VENDOR_ID,
+ HdmiUtils.getDefaultDeviceName(sourceAddress));
+ addCecDevice(newDevice);
+ }
+
+ switch (message.getOpcode()) {
+ case Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS:
+ handleReportPhysicalAddress(message);
+ break;
+ case Constants.MESSAGE_REPORT_POWER_STATUS:
+ handleReportPowerStatus(message);
+ break;
+ case Constants.MESSAGE_SET_OSD_NAME:
+ handleSetOsdName(message);
+ break;
+ case Constants.MESSAGE_DEVICE_VENDOR_ID:
+ handleDeviceVendorId(message);
+ break;
+
+ }
+ }
+
+ @ServiceThreadOnly
+ private void handleReportPhysicalAddress(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ int logicalAddress = message.getSource();
+ int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
+ int type = message.getParams()[2];
+
+ if (updateCecSwitchInfo(logicalAddress, type, physicalAddress)) return;
+
+ HdmiDeviceInfo deviceInfo = getCecDeviceInfo(logicalAddress);
+ if (deviceInfo == null) {
+ Slog.i(TAG, "Unknown source device info for <Report Physical Address> " + message);
+ } else {
+ HdmiDeviceInfo updatedDeviceInfo = new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
+ physicalAddress,
+ physicalAddressToPortId(physicalAddress), type, deviceInfo.getVendorId(),
+ deviceInfo.getDisplayName(), deviceInfo.getDevicePowerStatus());
+ updateCecDevice(updatedDeviceInfo);
+ }
+ }
+
+ @ServiceThreadOnly
+ private void handleReportPowerStatus(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ // Update power status of device
+ int newStatus = message.getParams()[0] & 0xFF;
+ updateDevicePowerStatus(message.getSource(), newStatus);
+ }
+
+ @ServiceThreadOnly
+ private void handleSetOsdName(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ int logicalAddress = message.getSource();
+ String osdName;
+ HdmiDeviceInfo deviceInfo = getCecDeviceInfo(logicalAddress);
+ // If the device is not in device list, ignore it.
+ if (deviceInfo == null) {
+ Slog.i(TAG, "No source device info for <Set Osd Name>." + message);
+ return;
+ }
+ try {
+ osdName = new String(message.getParams(), "US-ASCII");
+ } catch (UnsupportedEncodingException e) {
+ Slog.e(TAG, "Invalid <Set Osd Name> request:" + message, e);
+ return;
+ }
+
+ if (deviceInfo.getDisplayName() != null
+ && deviceInfo.getDisplayName().equals(osdName)) {
+ Slog.d(TAG, "Ignore incoming <Set Osd Name> having same osd name:" + message);
+ return;
+ }
+
+ Slog.d(TAG, "Updating device OSD name from "
+ + deviceInfo.getDisplayName()
+ + " to " + osdName);
+ updateCecDevice(new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
+ deviceInfo.getPhysicalAddress(), deviceInfo.getPortId(),
+ deviceInfo.getDeviceType(), deviceInfo.getVendorId(), osdName,
+ deviceInfo.getDevicePowerStatus()));
+ }
+
+ @ServiceThreadOnly
+ private void handleDeviceVendorId(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ int logicalAddress = message.getSource();
+ int vendorId = HdmiUtils.threeBytesToInt(message.getParams());
+
+ HdmiDeviceInfo deviceInfo = getCecDeviceInfo(logicalAddress);
+ if (deviceInfo == null) {
+ Slog.i(TAG, "Unknown source device info for <Device Vendor ID> " + message);
+ } else {
+ HdmiDeviceInfo updatedDeviceInfo = new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
+ deviceInfo.getPhysicalAddress(),
+ deviceInfo.getPortId(), deviceInfo.getDeviceType(), vendorId,
+ deviceInfo.getDisplayName(), deviceInfo.getDevicePowerStatus());
+ updateCecDevice(updatedDeviceInfo);
+ }
+ }
+
+ void addCecSwitch(int physicalAddress) {
+ mCecSwitches.add(physicalAddress);
+ }
+
+ public ArraySet<Integer> getCecSwitches() {
+ return mCecSwitches;
+ }
+
+ void removeDevicesConnectedToPort(int portId) {
+ Iterator<Integer> it = mCecSwitches.iterator();
+ while (it.hasNext()) {
+ int path = it.next();
+ int devicePortId = physicalAddressToPortId(path);
+ if (devicePortId == portId || devicePortId == Constants.INVALID_PORT_ID) {
+ it.remove();
+ }
+ }
+ List<Integer> toRemove = new ArrayList<>();
+ for (int i = 0; i < mDeviceInfos.size(); i++) {
+ int key = mDeviceInfos.keyAt(i);
+ int physicalAddress = mDeviceInfos.get(key).getPhysicalAddress();
+ int devicePortId = physicalAddressToPortId(physicalAddress);
+ if (devicePortId == portId || devicePortId == Constants.INVALID_PORT_ID) {
+ toRemove.add(key);
+ }
+ }
+ for (Integer key : toRemove) {
+ removeDeviceInfo(key);
+ }
+ }
+
+ boolean updateCecSwitchInfo(int address, int type, int path) {
+ if (address == Constants.ADDR_UNREGISTERED
+ && type == HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH) {
+ mCecSwitches.add(path);
+ updateSafeDeviceInfoList();
+ return true; // Pure switch does not need further processing. Return here.
+ }
+ if (type == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) {
+ mCecSwitches.add(path);
+ }
+ return false;
+ }
+
+ @GuardedBy("mLock")
+ List<HdmiDeviceInfo> getSafeCecDevicesLocked() {
+ ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
+ for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
+ if (isLocalDeviceAddress(info.getLogicalAddress())) {
+ continue;
+ }
+ infoList.add(info);
+ }
+ return infoList;
+ }
+
+ /**
+ * Thread safe version of {@link #getCecDeviceInfo(int)}.
+ *
+ * @param logicalAddress logical address to be retrieved
+ * @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
+ * Returns null if no logical address matched
+ */
+ HdmiDeviceInfo getSafeCecDeviceInfo(int logicalAddress) {
+ for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
+ if (info.isCecDevice() && info.getLogicalAddress() == logicalAddress) {
+ return info;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the {@link HdmiDeviceInfo} instance whose physical address matches
+ *
+ *
+ *
+ * qq * the given routing path. CEC devices use routing path for its physical address to
+ * describe the hierarchy of the devices in the network.
+ *
+ * @param path routing path or physical address
+ * @return {@link HdmiDeviceInfo} if the matched info is found; otherwise null
+ */
+ @ServiceThreadOnly
+ final HdmiDeviceInfo getDeviceInfoByPath(int path) {
+ assertRunOnServiceThread();
+ for (HdmiDeviceInfo info : getDeviceInfoList(false)) {
+ if (info.getPhysicalAddress() == path) {
+ return info;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the {@link HdmiDeviceInfo} instance whose physical address matches
+ * the given routing path. This is the version accessible safely from threads
+ * other than service thread.
+ *
+ * @param path routing path or physical address
+ * @return {@link HdmiDeviceInfo} if the matched info is found; otherwise null
+ */
+ HdmiDeviceInfo getSafeDeviceInfoByPath(int path) {
+ for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
+ if (info.getPhysicalAddress() == path) {
+ return info;
+ }
+ }
+ return null;
+ }
+
+ public int getPhysicalAddress() {
+ return mHdmiCecController.getPhysicalAddress();
+ }
+
+ @ServiceThreadOnly
+ public void clear() {
+ assertRunOnServiceThread();
+ initPortInfo();
+ clearDeviceList();
+ clearLocalDevices();
+ }
+
+ @ServiceThreadOnly
+ public void clearDeviceList() {
+ assertRunOnServiceThread();
+ for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) {
+ if (info.getPhysicalAddress() == getPhysicalAddress()) {
+ continue;
+ }
+ invokeDeviceEventListener(info,
+ HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
+ }
+ mDeviceInfos.clear();
+ updateSafeDeviceInfoList();
+ }
+
+ /**
+ * Returns HDMI port information for the given port id.
+ *
+ * @param portId HDMI port id
+ * @return {@link HdmiPortInfo} for the given port
+ */
+ HdmiPortInfo getPortInfo(int portId) {
+ return mPortInfoMap.get(portId, null);
+ }
+
+ /**
+ * Returns the routing path (physical address) of the HDMI port for the given
+ * port id.
+ */
+ int portIdToPath(int portId) {
+ HdmiPortInfo portInfo = getPortInfo(portId);
+ if (portInfo == null) {
+ Slog.e(TAG, "Cannot find the port info: " + portId);
+ return Constants.INVALID_PHYSICAL_ADDRESS;
+ }
+ return portInfo.getAddress();
+ }
+
+ /**
+ * Returns the id of HDMI port located at the current device that runs this method.
+ *
+ * For TV with physical address 0x0000, target device 0x1120, we want port physical address
+ * 0x1000 to get the correct port id from {@link #mPortIdMap}. For device with Physical Address
+ * 0x2000, target device 0x2420, we want port address 0x24000 to get the port id.
+ *
+ * <p>Return {@link Constants#INVALID_PORT_ID} if target device does not connect to.
+ *
+ * @param path the target device's physical address.
+ * @return the id of the port that the target device eventually connects to
+ * on the current device.
+ */
+ int physicalAddressToPortId(int path) {
+ int mask = 0xF000;
+ int finalMask = 0xF000;
+ int physicalAddress;
+ physicalAddress = getPhysicalAddress();
+ int maskedAddress = physicalAddress;
+
+ while (maskedAddress != 0) {
+ maskedAddress = physicalAddress & mask;
+ finalMask |= mask;
+ mask >>= 4;
+ }
+
+ int portAddress = path & finalMask;
+ return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID);
+ }
+
+ List<HdmiPortInfo> getPortInfo() {
+ return mPortInfo;
+ }
+
+ void setPortInfo(List<HdmiPortInfo> portInfo) {
+ mPortInfo = portInfo;
+ }
+
+ private boolean isLocalDeviceAddress(int address) {
+ for (int i = 0; i < mLocalDevices.size(); i++) {
+ int key = mLocalDevices.keyAt(i);
+ if (mLocalDevices.get(key).mAddress == address) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void assertRunOnServiceThread() {
+ if (Looper.myLooper() != mHandler.getLooper()) {
+ throw new IllegalStateException("Should run on service thread.");
+ }
+ }
+
+ protected void dump(IndentingPrintWriter pw) {
+ pw.println("HDMI CEC Network");
+ pw.increaseIndent();
+ HdmiUtils.dumpIterable(pw, "mPortInfo:", mPortInfo);
+ for (int i = 0; i < mLocalDevices.size(); ++i) {
+ pw.println("HdmiCecLocalDevice #" + mLocalDevices.keyAt(i) + ":");
+ pw.increaseIndent();
+ mLocalDevices.valueAt(i).dump(pw);
+
+ pw.println("Active Source history:");
+ pw.increaseIndent();
+ final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ ArrayBlockingQueue<HdmiCecController.Dumpable> activeSourceHistory =
+ mLocalDevices.valueAt(i).getActiveSourceHistory();
+ for (HdmiCecController.Dumpable activeSourceEvent : activeSourceHistory) {
+ activeSourceEvent.dump(pw, sdf);
+ }
+ pw.decreaseIndent();
+ pw.decreaseIndent();
+ }
+ HdmiUtils.dumpIterable(pw, "mDeviceInfos:", mSafeAllDeviceInfos);
+ pw.decreaseIndent();
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 27bd056..ee86593 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -71,10 +71,8 @@
import android.provider.Settings.Global;
import android.sysprop.HdmiProperties;
import android.text.TextUtils;
-import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -91,14 +89,15 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -175,6 +174,8 @@
static final int STANDBY_SCREEN_OFF = 0;
static final int STANDBY_SHUTDOWN = 1;
+ private HdmiCecNetwork mHdmiCecNetwork;
+
// Logical address of the active source.
@GuardedBy("mLock")
protected final ActiveSource mActiveSource = new ActiveSource();
@@ -187,6 +188,9 @@
@GuardedBy("mLock")
private boolean mHdmiCecVolumeControlEnabled;
+ // Make sure HdmiCecConfig is instantiated and the XMLs are read.
+ private final HdmiCecConfig mHdmiCecConfig;
+
/**
* Interface to report send result.
*/
@@ -328,21 +332,6 @@
@Nullable
private HdmiCecController mCecController;
- // HDMI port information. Stored in the unmodifiable list to keep the static information
- // from being modified.
- // This variable is null if the current device does not have hdmi input.
- @GuardedBy("mLock")
- private List<HdmiPortInfo> mPortInfo = null;
-
- // Map from path(physical address) to port ID.
- private UnmodifiableSparseIntArray mPortIdMap;
-
- // Map from port ID to HdmiPortInfo.
- private UnmodifiableSparseArray<HdmiPortInfo> mPortInfoMap;
-
- // Map from port ID to HdmiDeviceInfo.
- private UnmodifiableSparseArray<HdmiDeviceInfo> mPortDeviceMap;
-
private HdmiCecMessageValidator mMessageValidator;
@ServiceThreadOnly
@@ -384,10 +373,6 @@
@Nullable
private Looper mIoLooper;
- // Thread safe physical address
- @GuardedBy("mLock")
- private int mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
-
// Last input port before switching to the MHL port. Should switch back to this port
// when the mobile device sends the request one touch play with off.
// Gets invalidated if we go to other port/input.
@@ -483,6 +468,7 @@
}
mLocalDevices = deviceTypes;
mSettingsObserver = new SettingsObserver(mHandler);
+ mHdmiCecConfig = new HdmiCecConfig(context);
}
protected static List<Integer> getIntList(String string) {
@@ -501,42 +487,7 @@
@Override
public void onStart() {
- if (mIoLooper == null) {
- mIoThread.start();
- mIoLooper = mIoThread.getLooper();
- }
- mPowerStatus = getInitialPowerStatus();
- mProhibitMode = false;
- mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true);
- mHdmiCecVolumeControlEnabled = readBooleanSetting(
- Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true);
- mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true);
-
- if (mCecController == null) {
- mCecController = HdmiCecController.create(this, getAtomWriter());
- }
- if (mCecController != null) {
- if (mHdmiControlEnabled) {
- initializeCec(INITIATED_BY_BOOT_UP);
- } else {
- mCecController.setOption(OptionKey.ENABLE_CEC, false);
- }
- } else {
- Slog.i(TAG, "Device does not support HDMI-CEC.");
- return;
- }
- if (mMhlController == null) {
- mMhlController = HdmiMhlControllerStub.create(this);
- }
- if (!mMhlController.isReady()) {
- Slog.i(TAG, "Device does not support MHL-control.");
- }
- mMhlDevices = Collections.emptyList();
-
- initPortInfo();
- if (mMessageValidator == null) {
- mMessageValidator = new HdmiCecMessageValidator(this);
- }
+ initService();
publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
if (mCecController != null) {
@@ -554,6 +505,46 @@
mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, ENABLED);
}
+ @VisibleForTesting
+ void initService() {
+ if (mIoLooper == null) {
+ mIoThread.start();
+ mIoLooper = mIoThread.getLooper();
+ }
+ mPowerStatus = getInitialPowerStatus();
+ mProhibitMode = false;
+ mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true);
+ mHdmiCecVolumeControlEnabled = readBooleanSetting(
+ Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true);
+ mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true);
+
+ if (mCecController == null) {
+ mCecController = HdmiCecController.create(this, getAtomWriter());
+ }
+ if (mCecController == null) {
+ Slog.i(TAG, "Device does not support HDMI-CEC.");
+ return;
+ }
+ if (mMhlController == null) {
+ mMhlController = HdmiMhlControllerStub.create(this);
+ }
+ if (!mMhlController.isReady()) {
+ Slog.i(TAG, "Device does not support MHL-control.");
+ }
+ mHdmiCecNetwork = new HdmiCecNetwork(this, mCecController, mMhlController);
+ if (mHdmiControlEnabled) {
+ initializeCec(INITIATED_BY_BOOT_UP);
+ } else {
+ mCecController.setOption(OptionKey.ENABLE_CEC, false);
+ }
+ mMhlDevices = Collections.emptyList();
+
+ mHdmiCecNetwork.initPortInfo();
+ if (mMessageValidator == null) {
+ mMessageValidator = new HdmiCecMessageValidator(this);
+ }
+ }
+
private void bootCompleted() {
// on boot, if device is interactive, set HDMI CEC state as powered on as well
if (mPowerManager.isInteractive() && isPowerStandbyOrTransient()) {
@@ -582,6 +573,15 @@
}
@VisibleForTesting
+ void setHdmiCecNetwork(HdmiCecNetwork hdmiCecNetwork) {
+ mHdmiCecNetwork = hdmiCecNetwork;
+ }
+
+ public HdmiCecNetwork getHdmiCecNetwork() {
+ return mHdmiCecNetwork;
+ }
+
+ @VisibleForTesting
void setHdmiMhlController(HdmiMhlControllerStub hdmiMhlController) {
mMhlController = hdmiMhlController;
}
@@ -699,7 +699,7 @@
break;
case Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED:
for (int type : mLocalDevices) {
- HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);
+ HdmiCecLocalDevice localDevice = mHdmiCecNetwork.getLocalDevice(type);
if (localDevice != null) {
localDevice.setAutoDeviceOff(enabled);
}
@@ -794,7 +794,7 @@
// A container for [Device type, Local device info].
ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
for (int type : mLocalDevices) {
- HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);
+ HdmiCecLocalDevice localDevice = mHdmiCecNetwork.getLocalDevice(type);
if (localDevice == null) {
localDevice = HdmiCecLocalDevice.create(this, type);
}
@@ -826,43 +826,48 @@
for (final HdmiCecLocalDevice localDevice : allocatingDevices) {
mCecController.allocateLogicalAddress(localDevice.getType(),
localDevice.getPreferredAddress(), new AllocateAddressCallback() {
- @Override
- public void onAllocated(int deviceType, int logicalAddress) {
- if (logicalAddress == Constants.ADDR_UNREGISTERED) {
- Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
- } else {
- // Set POWER_STATUS_ON to all local devices because they share lifetime
- // with system.
- HdmiDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType,
- HdmiControlManager.POWER_STATUS_ON);
- localDevice.setDeviceInfo(deviceInfo);
- mCecController.addLocalDevice(deviceType, localDevice);
- mCecController.addLogicalAddress(logicalAddress);
- allocatedDevices.add(localDevice);
- }
+ @Override
+ public void onAllocated(int deviceType, int logicalAddress) {
+ if (logicalAddress == Constants.ADDR_UNREGISTERED) {
+ Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType
+ + "]");
+ } else {
+ // Set POWER_STATUS_ON to all local devices because they share
+ // lifetime
+ // with system.
+ HdmiDeviceInfo deviceInfo = createDeviceInfo(logicalAddress,
+ deviceType,
+ HdmiControlManager.POWER_STATUS_ON);
+ localDevice.setDeviceInfo(deviceInfo);
+ mHdmiCecNetwork.addLocalDevice(deviceType, localDevice);
+ mCecController.addLogicalAddress(logicalAddress);
+ allocatedDevices.add(localDevice);
+ }
- // Address allocation completed for all devices. Notify each device.
- if (allocatingDevices.size() == ++finished[0]) {
- mAddressAllocated = true;
- if (initiatedBy != INITIATED_BY_HOTPLUG) {
- // In case of the hotplug we don't call onInitializeCecComplete()
- // since we reallocate the logical address only.
- onInitializeCecComplete(initiatedBy);
+ // Address allocation completed for all devices. Notify each device.
+ if (allocatingDevices.size() == ++finished[0]) {
+ mAddressAllocated = true;
+ if (initiatedBy != INITIATED_BY_HOTPLUG) {
+ // In case of the hotplug we don't call
+ // onInitializeCecComplete()
+ // since we reallocate the logical address only.
+ onInitializeCecComplete(initiatedBy);
+ }
+ notifyAddressAllocated(allocatedDevices, initiatedBy);
+ // Reinvoke the saved display status callback once the local
+ // device is ready.
+ if (mDisplayStatusCallback != null) {
+ queryDisplayStatus(mDisplayStatusCallback);
+ mDisplayStatusCallback = null;
+ }
+ if (mOtpCallbackPendingAddressAllocation != null) {
+ oneTouchPlay(mOtpCallbackPendingAddressAllocation);
+ mOtpCallbackPendingAddressAllocation = null;
+ }
+ mCecMessageBuffer.processMessages();
+ }
}
- notifyAddressAllocated(allocatedDevices, initiatedBy);
- // Reinvoke the saved display status callback once the local device is ready.
- if (mDisplayStatusCallback != null) {
- queryDisplayStatus(mDisplayStatusCallback);
- mDisplayStatusCallback = null;
- }
- if (mOtpCallbackPendingAddressAllocation != null) {
- oneTouchPlay(mOtpCallbackPendingAddressAllocation);
- mOtpCallbackPendingAddressAllocation = null;
- }
- mCecMessageBuffer.processMessages();
- }
- }
- });
+ });
}
}
@@ -882,88 +887,14 @@
return mAddressAllocated;
}
- // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
- // keep them in one place.
- @ServiceThreadOnly
- @VisibleForTesting
- protected void initPortInfo() {
- assertRunOnServiceThread();
- HdmiPortInfo[] cecPortInfo = null;
-
- synchronized (mLock) {
- mPhysicalAddress = getPhysicalAddress();
- }
-
- // CEC HAL provides majority of the info while MHL does only MHL support flag for
- // each port. Return empty array if CEC HAL didn't provide the info.
- if (mCecController != null) {
- cecPortInfo = mCecController.getPortInfos();
- }
- if (cecPortInfo == null) {
- return;
- }
-
- SparseArray<HdmiPortInfo> portInfoMap = new SparseArray<>();
- SparseIntArray portIdMap = new SparseIntArray();
- SparseArray<HdmiDeviceInfo> portDeviceMap = new SparseArray<>();
- for (HdmiPortInfo info : cecPortInfo) {
- portIdMap.put(info.getAddress(), info.getId());
- portInfoMap.put(info.getId(), info);
- portDeviceMap.put(info.getId(), new HdmiDeviceInfo(info.getAddress(), info.getId()));
- }
- mPortIdMap = new UnmodifiableSparseIntArray(portIdMap);
- mPortInfoMap = new UnmodifiableSparseArray<>(portInfoMap);
- mPortDeviceMap = new UnmodifiableSparseArray<>(portDeviceMap);
-
- if (mMhlController == null) {
- return;
- }
- HdmiPortInfo[] mhlPortInfo = mMhlController.getPortInfos();
- ArraySet<Integer> mhlSupportedPorts = new ArraySet<Integer>(mhlPortInfo.length);
- for (HdmiPortInfo info : mhlPortInfo) {
- if (info.isMhlSupported()) {
- mhlSupportedPorts.add(info.getId());
- }
- }
-
- // Build HDMI port info list with CEC port info plus MHL supported flag. We can just use
- // cec port info if we do not have have port that supports MHL.
- if (mhlSupportedPorts.isEmpty()) {
- setPortInfo(Collections.unmodifiableList(Arrays.asList(cecPortInfo)));
- return;
- }
- ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
- for (HdmiPortInfo info : cecPortInfo) {
- if (mhlSupportedPorts.contains(info.getId())) {
- result.add(new HdmiPortInfo(info.getId(), info.getType(), info.getAddress(),
- info.isCecSupported(), true, info.isArcSupported()));
- } else {
- result.add(info);
- }
- }
- setPortInfo(Collections.unmodifiableList(result));
- }
-
List<HdmiPortInfo> getPortInfo() {
synchronized (mLock) {
- return mPortInfo;
+ return mHdmiCecNetwork.getPortInfo();
}
}
- void setPortInfo(List<HdmiPortInfo> portInfo) {
- synchronized (mLock) {
- mPortInfo = portInfo;
- }
- }
-
- /**
- * Returns HDMI port information for the given port id.
- *
- * @param portId HDMI port id
- * @return {@link HdmiPortInfo} for the given port
- */
HdmiPortInfo getPortInfo(int portId) {
- return mPortInfoMap.get(portId, null);
+ return mHdmiCecNetwork.getPortInfo(portId);
}
/**
@@ -971,12 +902,7 @@
* port id.
*/
int portIdToPath(int portId) {
- HdmiPortInfo portInfo = getPortInfo(portId);
- if (portInfo == null) {
- Slog.e(TAG, "Cannot find the port info: " + portId);
- return Constants.INVALID_PHYSICAL_ADDRESS;
- }
- return portInfo.getAddress();
+ return mHdmiCecNetwork.portIdToPath(portId);
}
/**
@@ -993,26 +919,11 @@
* on the current device.
*/
int pathToPortId(int path) {
- int mask = 0xF000;
- int finalMask = 0xF000;
- int physicalAddress;
- synchronized (mLock) {
- physicalAddress = mPhysicalAddress;
- }
- int maskedAddress = physicalAddress;
-
- while (maskedAddress != 0) {
- maskedAddress = physicalAddress & mask;
- finalMask |= mask;
- mask >>= 4;
- }
-
- int portAddress = path & finalMask;
- return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID);
+ return mHdmiCecNetwork.physicalAddressToPortId(path);
}
boolean isValidPortId(int portId) {
- return getPortInfo(portId) != null;
+ return mHdmiCecNetwork.getPortInfo(portId) != null;
}
/**
@@ -1062,7 +973,7 @@
@ServiceThreadOnly
HdmiDeviceInfo getDeviceInfo(int logicalAddress) {
assertRunOnServiceThread();
- return tv() == null ? null : tv().getCecDeviceInfo(logicalAddress);
+ return mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
}
@ServiceThreadOnly
@@ -1086,11 +997,7 @@
* Whether a device of the specified physical address is connected to ARC enabled port.
*/
boolean isConnectedToArcPort(int physicalAddress) {
- int portId = pathToPortId(physicalAddress);
- if (portId != Constants.INVALID_PORT_ID) {
- return mPortInfoMap.get(portId).isArcSupported();
- }
- return false;
+ return mHdmiCecNetwork.isConnectedToArcPort(physicalAddress);
}
@ServiceThreadOnly
@@ -1162,7 +1069,7 @@
}
return true;
}
-
+ getHdmiCecNetwork().handleCecMessage(message);
if (dispatchMessageToLocalDevice(message)) {
return true;
}
@@ -1177,7 +1084,7 @@
@ServiceThreadOnly
private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
assertRunOnServiceThread();
- for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
+ for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
if (device.dispatchMessage(message)
&& message.getDestination() != Constants.ADDR_BROADCAST) {
return true;
@@ -1203,12 +1110,12 @@
if (connected && !isTvDevice()
&& getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {
if (isSwitchDevice()) {
- initPortInfo();
+ mHdmiCecNetwork.initPortInfo();
HdmiLogger.debug("initPortInfo for switch device when onHotplug from tx.");
}
ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
for (int type : mLocalDevices) {
- HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);
+ HdmiCecLocalDevice localDevice = mHdmiCecNetwork.getLocalDevice(type);
if (localDevice == null) {
localDevice = HdmiCecLocalDevice.create(this, type);
localDevice.init();
@@ -1218,9 +1125,14 @@
allocateLogicalAddress(localDevices, INITIATED_BY_HOTPLUG);
}
- for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
+ for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
device.onHotplug(portId, connected);
}
+
+ if (!connected) {
+ mHdmiCecNetwork.removeDevicesConnectedToPort(portId);
+ }
+
announceHotplugEvent(portId, connected);
}
@@ -1256,7 +1168,7 @@
List<HdmiCecLocalDevice> getAllLocalDevices() {
assertRunOnServiceThread();
- return mCecController.getLocalDeviceList();
+ return mHdmiCecNetwork.getLocalDeviceList();
}
/**
@@ -1269,8 +1181,14 @@
*
* @param logicalAddress logical address of the remote device that might have the same logical
* address as the current device.
+ * @param physicalAddress physical address of the given device.
*/
- protected void checkLogicalAddressConflictAndReallocate(int logicalAddress) {
+ protected void checkLogicalAddressConflictAndReallocate(int logicalAddress,
+ int physicalAddress) {
+ // The given device is a local device. No logical address conflict.
+ if (physicalAddress == getPhysicalAddress()) {
+ return;
+ }
for (HdmiCecLocalDevice device : getAllLocalDevices()) {
if (device.getDeviceInfo().getLogicalAddress() == logicalAddress) {
HdmiLogger.debug("allocate logical address for " + device.getDeviceInfo());
@@ -1610,8 +1528,7 @@
return null;
}
if (audioSystem() != null) {
- HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
- for (HdmiDeviceInfo info : audioSystem.getSafeCecDevicesLocked()) {
+ for (HdmiDeviceInfo info : mHdmiCecNetwork.getSafeCecDevicesLocked()) {
if (info.getPhysicalAddress() == activeSource.physicalAddress) {
return info;
}
@@ -1643,7 +1560,7 @@
}
int activePath = tv.getActivePath();
if (activePath != HdmiDeviceInfo.PATH_INVALID) {
- HdmiDeviceInfo info = tv.getSafeDeviceInfoByPath(activePath);
+ HdmiDeviceInfo info = mHdmiCecNetwork.getSafeDeviceInfoByPath(activePath);
return (info != null) ? info : new HdmiDeviceInfo(activePath, tv.getActivePortId());
}
return null;
@@ -1746,7 +1663,7 @@
return;
}
if (mCecController != null) {
- HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType);
+ HdmiCecLocalDevice localDevice = mHdmiCecNetwork.getLocalDevice(deviceType);
if (localDevice == null) {
Slog.w(TAG, "Local device not available to send key event.");
return;
@@ -1768,7 +1685,7 @@
Slog.w(TAG, "CEC controller not available to send volume key event.");
return;
}
- HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType);
+ HdmiCecLocalDevice localDevice = mHdmiCecNetwork.getLocalDevice(deviceType);
if (localDevice == null) {
Slog.w(TAG, "Local device " + deviceType
+ " not available to send volume key event.");
@@ -1882,7 +1799,7 @@
public int getPhysicalAddress() {
enforceAccessPermission();
synchronized (mLock) {
- return mPhysicalAddress;
+ return mHdmiCecNetwork.getPhysicalAddress();
}
}
@@ -1928,13 +1845,8 @@
enforceAccessPermission();
// No need to hold the lock for obtaining TV device as the local device instance
// is preserved while the HDMI control is enabled.
- HdmiCecLocalDeviceTv tv = tv();
- synchronized (mLock) {
- List<HdmiDeviceInfo> cecDevices = (tv == null)
- ? Collections.<HdmiDeviceInfo>emptyList()
- : tv.getSafeExternalInputsLocked();
- return HdmiUtils.mergeToUnmodifiableList(cecDevices, getMhlDevicesLocked());
- }
+ return HdmiUtils.mergeToUnmodifiableList(mHdmiCecNetwork.getSafeExternalInputsLocked(),
+ getMhlDevicesLocked());
}
// Returns all the CEC devices on the bus including system audio, switch,
@@ -1942,19 +1854,7 @@
@Override
public List<HdmiDeviceInfo> getDeviceList() {
enforceAccessPermission();
- HdmiCecLocalDeviceTv tv = tv();
- if (tv != null) {
- synchronized (mLock) {
- return tv.getSafeCecDevicesLocked();
- }
- } else {
- HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
- synchronized (mLock) {
- return (audioSystem == null)
- ? Collections.<HdmiDeviceInfo>emptyList()
- : audioSystem.getSafeCecDevicesLocked();
- }
- }
+ return mHdmiCecNetwork.getSafeCecDevicesLocked();
}
@Override
@@ -2083,7 +1983,7 @@
runOnServiceThread(new Runnable() {
@Override
public void run() {
- HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
+ HdmiCecLocalDevice device = mHdmiCecNetwork.getLocalDevice(deviceType);
if (device == null) {
Slog.w(TAG, "Local device not available");
return;
@@ -2111,7 +2011,7 @@
mhlDevice.sendStandby();
return;
}
- HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
+ HdmiCecLocalDevice device = mHdmiCecNetwork.getLocalDevice(deviceType);
if (device == null) {
device = audioSystem();
}
@@ -2256,7 +2156,7 @@
runOnServiceThread(new Runnable() {
@Override
public void run() {
- HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
+ HdmiCecLocalDevice device = mHdmiCecNetwork.getLocalDevice(deviceType);
if (device == null) {
Slog.w(TAG, "Local device not available");
return;
@@ -2316,12 +2216,24 @@
pw.println("mHdmiCecVolumeControlEnabled: " + mHdmiCecVolumeControlEnabled);
pw.decreaseIndent();
+ // CEC settings
+ pw.println("CEC settings:");
+ pw.increaseIndent();
+ HdmiCecConfig hdmiCecConfig = HdmiControlService.this.getHdmiCecConfig();
+ List<String> allSettings = hdmiCecConfig.getAllSettings();
+ Set<String> userSettings = new HashSet<>(hdmiCecConfig.getUserSettings());
+ for (String setting : allSettings) {
+ pw.println(setting + ": " + hdmiCecConfig.getValue(setting)
+ + " (default: " + hdmiCecConfig.getDefaultValue(setting) + ")"
+ + (userSettings.contains(setting) ? " [modifiable]" : ""));
+ }
+ pw.decreaseIndent();
+
pw.println("mMhlController: ");
pw.increaseIndent();
mMhlController.dump(pw);
pw.decreaseIndent();
-
- HdmiUtils.dumpIterable(pw, "mPortInfo:", mPortInfo);
+ mHdmiCecNetwork.dump(pw);
if (mCecController != null) {
pw.println("mCecController: ");
pw.increaseIndent();
@@ -2329,6 +2241,50 @@
pw.decreaseIndent();
}
}
+
+ @Override
+ public List<String> getUserCecSettings() {
+ enforceAccessPermission();
+ long token = Binder.clearCallingIdentity();
+ try {
+ return HdmiControlService.this.getHdmiCecConfig().getUserSettings();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public List<String> getAllowedCecSettingValues(String name) {
+ enforceAccessPermission();
+ long token = Binder.clearCallingIdentity();
+ try {
+ return HdmiControlService.this.getHdmiCecConfig().getAllowedValues(name);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public String getCecSettingValue(String name) {
+ enforceAccessPermission();
+ long token = Binder.clearCallingIdentity();
+ try {
+ return HdmiControlService.this.getHdmiCecConfig().getValue(name);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void setCecSettingValue(String name, String value) {
+ enforceAccessPermission();
+ long token = Binder.clearCallingIdentity();
+ try {
+ HdmiControlService.this.getHdmiCecConfig().setValue(name, value);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
}
// Get the source address to send out commands to devices connected to the current device
@@ -2769,7 +2725,7 @@
}
public HdmiCecLocalDeviceTv tv() {
- return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
+ return (HdmiCecLocalDeviceTv) mHdmiCecNetwork.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
}
boolean isTvDevice() {
@@ -2794,11 +2750,11 @@
protected HdmiCecLocalDevicePlayback playback() {
return (HdmiCecLocalDevicePlayback)
- mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK);
+ mHdmiCecNetwork.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK);
}
public HdmiCecLocalDeviceAudioSystem audioSystem() {
- return (HdmiCecLocalDeviceAudioSystem) mCecController.getLocalDevice(
+ return (HdmiCecLocalDeviceAudioSystem) mHdmiCecNetwork.getLocalDevice(
HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
}
@@ -2928,7 +2884,7 @@
}
private boolean canGoToStandby() {
- for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
+ for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
if (!device.canGoToStandby()) return false;
}
return true;
@@ -2962,7 +2918,7 @@
private void disableDevices(PendingActionClearedCallback callback) {
if (mCecController != null) {
- for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
+ for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
device.disableDevice(mStandbyMessageReceived, callback);
}
}
@@ -2976,7 +2932,8 @@
return;
}
mCecController.clearLogicalAddress();
- mCecController.clearLocalDevices();
+ mHdmiCecNetwork.clearLogicalAddress();
+ mHdmiCecNetwork.clearLocalDevices();
}
@ServiceThreadOnly
@@ -2988,7 +2945,7 @@
return;
}
mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
- for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
+ for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
device.onStandby(mStandbyMessageReceived, standbyAction);
}
mStandbyMessageReceived = false;
@@ -3348,7 +3305,7 @@
// input change listener should be the one describing the corresponding HDMI port.
HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
HdmiDeviceInfo info = (device != null) ? device.getInfo()
- : mPortDeviceMap.get(portId, HdmiDeviceInfo.INACTIVE_DEVICE);
+ : mHdmiCecNetwork.getDeviceForPortId(portId);
invokeInputChangeListener(info);
}
@@ -3389,4 +3346,8 @@
getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
HdmiControlService.PERMISSION);
}
+
+ HdmiCecConfig getHdmiCecConfig() {
+ return mHdmiCecConfig;
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
index 7670dcc..ece78bfa2 100644
--- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -148,7 +148,8 @@
}
private void checkHotplug(List<Integer> ackedAddress, boolean audioOnly) {
- BitSet currentInfos = infoListToBitSet(tv().getDeviceInfoList(false), audioOnly);
+ BitSet currentInfos = infoListToBitSet(
+ localDevice().mService.getHdmiCecNetwork().getDeviceInfoList(false), audioOnly);
BitSet polledResult = addressListToBitSet(ackedAddress);
// At first, check removed devices.
@@ -225,11 +226,11 @@
mayCancelOneTouchRecord(removedAddress);
mayDisableSystemAudioAndARC(removedAddress);
- tv().removeCecDevice(removedAddress);
+ localDevice().mService.getHdmiCecNetwork().removeCecDevice(localDevice(), removedAddress);
}
private void mayChangeRoutingPath(int address) {
- HdmiDeviceInfo info = tv().getCecDeviceInfo(address);
+ HdmiDeviceInfo info = localDevice().mService.getHdmiCecNetwork().getCecDeviceInfo(address);
if (info != null) {
tv().handleRemoveActiveRoutingPath(info.getPhysicalAddress());
}
diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
index 6753368..edc7bd9 100644
--- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java
+++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
@@ -19,6 +19,7 @@
import android.util.Slog;
import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
+
import java.io.UnsupportedEncodingException;
/**
@@ -164,7 +165,8 @@
private void addDeviceInfo() {
// The device should be in the device list with default information.
- if (!tv().isInDeviceList(mDeviceLogicalAddress, mDevicePhysicalAddress)) {
+ if (!localDevice().mService.getHdmiCecNetwork().isInDeviceList(mDeviceLogicalAddress,
+ mDevicePhysicalAddress)) {
Slog.w(TAG, String.format("Device not found (%02x, %04x)",
mDeviceLogicalAddress, mDevicePhysicalAddress));
return;
@@ -176,7 +178,7 @@
mDeviceLogicalAddress, mDevicePhysicalAddress,
tv().getPortId(mDevicePhysicalAddress),
mDeviceType, mVendorId, mDisplayName);
- tv().addCecDevice(deviceInfo);
+ localDevice().mService.getHdmiCecNetwork().addCecDevice(deviceInfo);
// Consume CEC messages we already got for this newly found device.
tv().processDelayedMessages(mDeviceLogicalAddress);
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index e78a86c..53f9a10 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -19,6 +19,7 @@
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
+import android.provider.Settings.Global;
import android.util.Slog;
import java.util.ArrayList;
@@ -54,6 +55,8 @@
private int mPowerStatusCounter = 0;
+ private HdmiCecLocalDeviceSource mSource;
+
// Factory method. Ensures arguments are valid.
static OneTouchPlayAction create(HdmiCecLocalDeviceSource source,
int targetAddress, IHdmiControlCallback callback) {
@@ -74,27 +77,33 @@
@Override
boolean start() {
+ // Because only source device can create this action, it's safe to cast.
+ mSource = source();
sendCommand(HdmiCecMessageBuilder.buildTextViewOn(getSourceAddress(), mTargetAddress));
broadcastActiveSource();
+ // If the device is not an audio system itself, request the connected audio system to
+ // turn on.
+ if (shouldTurnOnConnectedAudioSystem()) {
+ sendCommand(HdmiCecMessageBuilder.buildSystemAudioModeRequest(getSourceAddress(),
+ Constants.ADDR_AUDIO_SYSTEM, getSourcePath(), true));
+ }
queryDevicePowerStatus();
addTimer(mState, HdmiConfig.TIMEOUT_MS);
return true;
}
private void broadcastActiveSource() {
- // Because only source device can create this action, it's safe to cast.
- HdmiCecLocalDeviceSource source = source();
- source.mService.setAndBroadcastActiveSourceFromOneDeviceType(
+ mSource.mService.setAndBroadcastActiveSourceFromOneDeviceType(
mTargetAddress, getSourcePath(), "OneTouchPlayAction#broadcastActiveSource()");
// When OneTouchPlay is called, client side should be responsible to send out the intent
// of which internal source, for example YouTube, it would like to switch to.
// Here we only update the active port and the active source records in the local
// device as well as claiming Active Source.
- if (source.mService.audioSystem() != null) {
- source = source.mService.audioSystem();
+ if (mSource.mService.audioSystem() != null) {
+ mSource = mSource.mService.audioSystem();
}
- source.setRoutingPort(Constants.CEC_SWITCH_HOME);
- source.setLocalActivePort(Constants.CEC_SWITCH_HOME);
+ mSource.setRoutingPort(Constants.CEC_SWITCH_HOME);
+ mSource.setLocalActivePort(Constants.CEC_SWITCH_HOME);
}
private void queryDevicePowerStatus() {
@@ -151,4 +160,14 @@
Slog.e(TAG, "Callback failed:" + e);
}
}
+
+ private boolean shouldTurnOnConnectedAudioSystem() {
+ HdmiControlService service = mSource.mService;
+ if (service.isAudioSystemDevice()) {
+ return false;
+ }
+ String sendStandbyOnSleep = service.readStringSetting(
+ Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, "");
+ return sendStandbyOnSleep.equals(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
index a62d0b6..909fcda 100644
--- a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
+++ b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
@@ -20,7 +20,9 @@
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.util.SparseIntArray;
+
import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+
import java.util.List;
/**
@@ -111,7 +113,8 @@
}
private void queryPowerStatus() {
- List<HdmiDeviceInfo> deviceInfos = tv().getDeviceInfoList(false);
+ List<HdmiDeviceInfo> deviceInfos =
+ localDevice().mService.getHdmiCecNetwork().getDeviceInfoList(false);
resetPowerStatus(deviceInfos);
for (HdmiDeviceInfo info : deviceInfos) {
final int logicalAddress = info.getLogicalAddress();
@@ -137,7 +140,8 @@
}
private void updatePowerStatus(int logicalAddress, int newStatus, boolean remove) {
- tv().updateDevicePowerStatus(logicalAddress, newStatus);
+ localDevice().mService.getHdmiCecNetwork().updateDevicePowerStatus(logicalAddress,
+ newStatus);
if (remove) {
mPowerStatus.delete(logicalAddress);
diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
index 6c8694e..6c147ed 100644
--- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java
+++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
@@ -17,8 +17,8 @@
package com.android.server.hdmi;
import android.annotation.Nullable;
-import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;
@@ -160,7 +160,9 @@
}
switch (timeoutState) {
case STATE_WAIT_FOR_ROUTING_INFORMATION:
- HdmiDeviceInfo device = tv().getDeviceInfoByPath(mCurrentRoutingPath);
+ HdmiDeviceInfo device =
+ localDevice().mService.getHdmiCecNetwork().getDeviceInfoByPath(
+ mCurrentRoutingPath);
if (device != null && mQueryDevicePowerStatus) {
int deviceLogicalAddress = device.getLogicalAddress();
queryDevicePowerStatus(deviceLogicalAddress, new SendMessageCallback() {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 9947ecd..047f174 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -119,6 +119,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.LruCache;
import android.util.Pair;
@@ -161,7 +162,6 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.os.TransferPipe;
import com.android.internal.util.DumpUtils;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.IInlineSuggestionsResponseCallback;
import com.android.internal.view.IInputContext;
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index fb23e01..7a2e4ed 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -242,6 +242,19 @@
}
}, UserHandle.USER_ALL);
}
+
+ if (mContextHubWrapper.supportsAirplaneModeSettingNotifications()) {
+ sendAirplaneModeSettingUpdate();
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON),
+ true /* notifyForDescendants */,
+ new ContentObserver(null /* handler */) {
+ @Override
+ public void onChange(boolean selfChange) {
+ sendAirplaneModeSettingUpdate();
+ }
+ }, UserHandle.USER_ALL);
+ }
}
/**
@@ -614,6 +627,7 @@
if (eventType == AsyncEventType.RESTARTED) {
sendLocationSettingUpdate();
sendWifiSettingUpdate(true /* forceUpdate */);
+ sendAirplaneModeSettingUpdate();
mTransactionManager.onHubReset();
queryNanoAppsInternal(contextHubId);
@@ -958,6 +972,7 @@
/**
* Obtains the latest WiFi availability setting value and notifies the Context Hub.
+ *
* @param forceUpdate True to force send update to the Context Hub, otherwise only send the
* update when the WiFi availability changes.
*/
@@ -972,6 +987,17 @@
}
}
+ /**
+ * Obtains the latest airplane mode setting value and notifies the Context Hub.
+ */
+ private void sendAirplaneModeSettingUpdate() {
+ boolean enabled =
+ (Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0)
+ == 1);
+ mContextHubWrapper.onAirplaneModeSettingChanged(enabled);
+ }
+
private String getCallingPackageName() {
return mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
}
diff --git a/services/core/java/com/android/server/location/IContextHubWrapper.java b/services/core/java/com/android/server/location/IContextHubWrapper.java
index 613964a..9ac7c6b 100644
--- a/services/core/java/com/android/server/location/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/IContextHubWrapper.java
@@ -116,6 +116,19 @@
*/
public abstract void onWifiSettingChanged(boolean enabled);
+ /**
+ * @return True if this version of the Contexthub HAL supports airplane mode setting
+ * notifications.
+ */
+ public abstract boolean supportsAirplaneModeSettingNotifications();
+
+ /**
+ * Notifies the Contexthub implementation of an airplane mode setting change.
+ *
+ * @param enabled true if the airplane mode setting has been enabled.
+ */
+ public abstract void onAirplaneModeSettingChanged(boolean enabled);
+
private static class ContextHubWrapperV1_0 extends IContextHubWrapper {
private android.hardware.contexthub.V1_0.IContexthub mHub;
@@ -135,11 +148,18 @@
return false;
}
+ public boolean supportsAirplaneModeSettingNotifications() {
+ return false;
+ }
+
public void onLocationSettingChanged(boolean enabled) {
}
public void onWifiSettingChanged(boolean enabled) {
}
+
+ public void onAirplaneModeSettingChanged(boolean enabled) {
+ }
}
private static class ContextHubWrapperV1_1 extends IContextHubWrapper {
@@ -161,6 +181,10 @@
return false;
}
+ public boolean supportsAirplaneModeSettingNotifications() {
+ return false;
+ }
+
public void onLocationSettingChanged(boolean enabled) {
try {
mHub.onSettingChanged(Setting.LOCATION,
@@ -172,6 +196,9 @@
public void onWifiSettingChanged(boolean enabled) {
}
+
+ public void onAirplaneModeSettingChanged(boolean enabled) {
+ }
}
private static class ContextHubWrapperV1_2 extends IContextHubWrapper {
@@ -193,6 +220,10 @@
return true;
}
+ public boolean supportsAirplaneModeSettingNotifications() {
+ return true;
+ }
+
public void onLocationSettingChanged(boolean enabled) {
sendSettingChanged(Setting.LOCATION,
enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
@@ -203,6 +234,11 @@
enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
}
+ public void onAirplaneModeSettingChanged(boolean enabled) {
+ sendSettingChanged(android.hardware.contexthub.V1_2.Setting.AIRPLANE_MODE,
+ enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
+ }
+
private void sendSettingChanged(byte setting, byte newValue) {
try {
mHub.onSettingChanged_1_2(setting, newValue);
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index cdb73d8..9ca4d35 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -556,8 +556,8 @@
@Override
public void registerLocationListener(String provider, LocationRequest request,
- ILocationListener listener, String packageName, String attributionTag,
- String listenerId) {
+ ILocationListener listener, String packageName, @Nullable String attributionTag,
+ @Nullable String listenerId) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
listenerId);
int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
@@ -582,7 +582,7 @@
@Override
public void registerLocationPendingIntent(String provider, LocationRequest request,
- PendingIntent pendingIntent, String packageName, String attributionTag) {
+ PendingIntent pendingIntent, String packageName, @Nullable String attributionTag) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
AppOpsManager.toReceiverId(pendingIntent));
int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java
index 179fb7d..b4a1723 100644
--- a/services/core/java/com/android/server/location/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/LocationProviderManager.java
@@ -77,7 +77,6 @@
import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
import com.android.internal.util.Preconditions;
@@ -115,7 +114,6 @@
class LocationProviderManager extends
ListenerMultiplexer<Object, LocationProviderManager.LocationTransport,
- LocationProviderManager.LocationListenerOperation,
LocationProviderManager.Registration, ProviderRequest> implements
AbstractLocationProvider.Listener {
@@ -225,16 +223,8 @@
}
}
- protected interface LocationListenerOperation extends ListenerOperation<LocationTransport> {
- /**
- * Must be implemented to return the location this operation intends to deliver.
- */
- @Nullable
- Location getLocation();
- }
-
protected abstract class Registration extends RemoteListenerRegistration<LocationRequest,
- LocationTransport, LocationListenerOperation> {
+ LocationTransport> {
private final @PermissionLevel int mPermissionLevel;
@@ -310,7 +300,7 @@
protected void onProviderListenerUnregister() {}
@Override
- protected final LocationListenerOperation onActive() {
+ protected final void onActive() {
if (Build.IS_DEBUGGABLE) {
Preconditions.checkState(Thread.holdsLock(mLock));
}
@@ -319,11 +309,12 @@
mLocationAttributionHelper.reportLocationStart(getIdentity(), getName(), getKey());
}
onHighPowerUsageChanged();
- return onProviderListenerActive();
+
+ onProviderListenerActive();
}
@Override
- protected final LocationListenerOperation onInactive() {
+ protected final void onInactive() {
if (Build.IS_DEBUGGABLE) {
Preconditions.checkState(Thread.holdsLock(mLock));
}
@@ -332,24 +323,21 @@
if (!getRequest().isHiddenFromAppOps()) {
mLocationAttributionHelper.reportLocationStop(getIdentity(), getName(), getKey());
}
- return onProviderListenerInactive();
+
+ onProviderListenerInactive();
}
/**
* Subclasses may override this instead of {@link #onActive()}.
*/
@GuardedBy("mLock")
- protected LocationListenerOperation onProviderListenerActive() {
- return null;
- }
+ protected void onProviderListenerActive() {}
/**
* Subclasses may override this instead of {@link #onInactive()} ()}.
*/
@GuardedBy("mLock")
- protected LocationListenerOperation onProviderListenerInactive() {
- return null;
- }
+ protected void onProviderListenerInactive() {}
@Override
public final LocationRequest getRequest() {
@@ -357,10 +345,8 @@
}
@GuardedBy("mLock")
- final void initializeLastLocation(@Nullable Location location) {
- if (mLastLocation == null) {
- mLastLocation = location;
- }
+ final void setLastDeliveredLocation(@Nullable Location location) {
+ mLastLocation = location;
}
@GuardedBy("mLock")
@@ -541,16 +527,8 @@
}
@GuardedBy("mLock")
- @Override
- protected final LocationListenerOperation onExecuteOperation(
- LocationListenerOperation operation) {
- mLastLocation = operation.getLocation();
- return super.onExecuteOperation(operation);
- }
-
- @GuardedBy("mLock")
- @Nullable
- abstract LocationListenerOperation acceptLocationChange(Location fineLocation);
+ abstract @Nullable ListenerOperation<LocationTransport> acceptLocationChange(
+ Location fineLocation);
@Override
public String toString() {
@@ -656,7 +634,7 @@
@GuardedBy("mLock")
@Override
- protected final LocationListenerOperation onProviderListenerActive() {
+ protected final void onProviderListenerActive() {
// a new registration may not get a location immediately, the provider request may be
// delayed. therefore we deliver a historical location if available. since delivering an
// older location could be considered a breaking change for some applications, we only
@@ -679,12 +657,10 @@
getRequest().isLocationSettingsIgnored(),
maxLocationAgeMs);
if (lastLocation != null) {
- return acceptLocationChange(lastLocation);
+ executeOperation(acceptLocationChange(lastLocation));
}
}
}
-
- return null;
}
@Override
@@ -703,9 +679,9 @@
}
@GuardedBy("mLock")
- @Nullable
@Override
- LocationListenerOperation acceptLocationChange(Location fineLocation) {
+ @Nullable ListenerOperation<LocationTransport> acceptLocationChange(
+ Location fineLocation) {
if (Build.IS_DEBUGGABLE) {
Preconditions.checkState(Thread.holdsLock(mLock));
}
@@ -748,16 +724,20 @@
return null;
}
- return new LocationListenerOperation() {
- @Override
- public Location getLocation() {
- return location;
- }
+ // deliver location
+ return new ListenerOperation<LocationTransport>() {
+
+ private boolean mUseWakeLock;
@Override
public void onPreExecute() {
+ mUseWakeLock = !location.isFromMockProvider();
+
+ // update last delivered location
+ setLastDeliveredLocation(location);
+
// don't acquire a wakelock for mock locations to prevent abuse
- if (!location.isFromMockProvider()) {
+ if (mUseWakeLock) {
mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
}
}
@@ -774,13 +754,13 @@
}
listener.deliverOnLocationChanged(deliveryLocation,
- location.isFromMockProvider() ? null : mWakeLock::release);
+ mUseWakeLock ? mWakeLock::release : null);
mLocationEventLog.logProviderDeliveredLocation(mName, getIdentity());
}
@Override
public void onPostExecute(boolean success) {
- if (!success && !location.isFromMockProvider()) {
+ if (!success && mUseWakeLock) {
mWakeLock.release();
}
@@ -852,7 +832,8 @@
}
@Override
- public void onOperationFailure(LocationListenerOperation operation, Exception exception) {
+ public void onOperationFailure(ListenerOperation<LocationTransport> operation,
+ Exception exception) {
onTransportFailure(exception);
}
@@ -913,7 +894,8 @@
}
@Override
- public void onOperationFailure(LocationListenerOperation operation, Exception exception) {
+ public void onOperationFailure(ListenerOperation<LocationTransport> operation,
+ Exception exception) {
onTransportFailure(exception);
}
@@ -988,28 +970,24 @@
@GuardedBy("mLock")
@Override
- protected LocationListenerOperation onProviderListenerActive() {
+ protected void onProviderListenerActive() {
Location lastLocation = getLastLocationUnsafe(
getIdentity().getUserId(),
getPermissionLevel(),
getRequest().isLocationSettingsIgnored(),
MAX_CURRENT_LOCATION_AGE_MS);
if (lastLocation != null) {
- return acceptLocationChange(lastLocation);
+ executeOperation(acceptLocationChange(lastLocation));
}
-
- return null;
}
@GuardedBy("mLock")
@Override
- protected LocationListenerOperation onProviderListenerInactive() {
+ protected void onProviderListenerInactive() {
if (!getRequest().isLocationSettingsIgnored()) {
// if we go inactive for any reason, fail immediately
- return acceptLocationChange(null);
+ executeOperation(acceptLocationChange(null));
}
-
- return null;
}
void deliverNull() {
@@ -1035,9 +1013,9 @@
}
@GuardedBy("mLock")
- @Nullable
@Override
- LocationListenerOperation acceptLocationChange(@Nullable Location fineLocation) {
+ @Nullable ListenerOperation<LocationTransport> acceptLocationChange(
+ @Nullable Location fineLocation) {
if (Build.IS_DEBUGGABLE) {
Preconditions.checkState(Thread.holdsLock(mLock));
}
@@ -1059,36 +1037,21 @@
Location location = getPermittedLocation(fineLocation, getPermissionLevel());
- return new LocationListenerOperation() {
- @Override
- public Location getLocation() {
- return location;
+ // deliver location
+ return listener -> {
+ // if delivering to the same process, make a copy of the location first (since
+ // location is mutable)
+ Location deliveryLocation = location;
+ if (getIdentity().getPid() == Process.myPid() && location != null) {
+ deliveryLocation = new Location(location);
}
- @Override
- public void operate(LocationTransport listener) {
- // if delivering to the same process, make a copy of the location first (since
- // location is mutable)
- Location deliveryLocation = location;
- if (getIdentity().getPid() == Process.myPid() && location != null) {
- deliveryLocation = new Location(location);
- }
+ // we currently don't hold a wakelock for getCurrentLocation deliveries
+ listener.deliverOnLocationChanged(deliveryLocation, null);
+ mLocationEventLog.logProviderDeliveredLocation(mName, getIdentity());
- // we currently don't hold a wakelock for getCurrentLocation deliveries
- try {
- listener.deliverOnLocationChanged(deliveryLocation, null);
- mLocationEventLog.logProviderDeliveredLocation(mName, getIdentity());
- } catch (Exception exception) {
- if (exception instanceof RemoteException) {
- Log.w(TAG, "registration " + this + " failed", exception);
- } else {
- throw new AssertionError(exception);
- }
- }
-
- synchronized (mLock) {
- remove();
- }
+ synchronized (mLock) {
+ remove();
}
};
}
@@ -1114,7 +1077,7 @@
protected final Object mLock = new Object();
protected final String mName;
- @Nullable private final PassiveLocationProviderManager mPassiveManager;
+ private final @Nullable PassiveLocationProviderManager mPassiveManager;
protected final Context mContext;
@@ -1178,7 +1141,7 @@
protected final MockableLocationProvider mProvider;
@GuardedBy("mLock")
- @Nullable private OnAlarmListener mDelayedRegister;
+ private @Nullable OnAlarmListener mDelayedRegister;
LocationProviderManager(Context context, Injector injector, String name,
@Nullable PassiveLocationProviderManager passiveManager) {
@@ -1254,13 +1217,11 @@
return mName;
}
- @Nullable
- public CallerIdentity getIdentity() {
+ public @Nullable CallerIdentity getIdentity() {
return mProvider.getState().identity;
}
- @Nullable
- public ProviderProperties getProperties() {
+ public @Nullable ProviderProperties getProperties() {
return mProvider.getState().properties;
}
@@ -1381,9 +1342,8 @@
}
}
- @Nullable
- public Location getLastLocation(CallerIdentity identity, @PermissionLevel int permissionLevel,
- boolean ignoreLocationSettings) {
+ public @Nullable Location getLastLocation(CallerIdentity identity,
+ @PermissionLevel int permissionLevel, boolean ignoreLocationSettings) {
if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
identity.getPackageName())) {
return null;
@@ -1426,9 +1386,9 @@
* location, even if the permissionLevel is coarse. You are responsible for coarsening the
* location if necessary.
*/
- @Nullable
- public Location getLastLocationUnsafe(int userId, @PermissionLevel int permissionLevel,
- boolean ignoreLocationSettings, long maximumAgeMs) {
+ public @Nullable Location getLastLocationUnsafe(int userId,
+ @PermissionLevel int permissionLevel, boolean ignoreLocationSettings,
+ long maximumAgeMs) {
if (userId == UserHandle.USER_ALL) {
// find the most recent location across all users
Location lastLocation = null;
@@ -1500,8 +1460,7 @@
}
}
- @Nullable
- public ICancellationSignal getCurrentLocation(LocationRequest request,
+ public @Nullable ICancellationSignal getCurrentLocation(LocationRequest request,
CallerIdentity identity, int permissionLevel, ILocationCallback callback) {
if (request.getDurationMillis() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) {
request = new LocationRequest.Builder(request)
@@ -1519,7 +1478,7 @@
synchronized (mLock) {
final long ident = Binder.clearCallingIdentity();
try {
- addRegistration(callback.asBinder(), registration);
+ putRegistration(callback.asBinder(), registration);
if (!registration.isActive()) {
// if the registration never activated, fail it immediately
registration.deliverNull();
@@ -1560,7 +1519,7 @@
synchronized (mLock) {
final long ident = Binder.clearCallingIdentity();
try {
- addRegistration(listener.asBinder(), registration);
+ putRegistration(listener.asBinder(), registration);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1578,7 +1537,7 @@
synchronized (mLock) {
final long identity = Binder.clearCallingIdentity();
try {
- addRegistration(pendingIntent, registration);
+ putRegistration(pendingIntent, registration);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -1673,7 +1632,7 @@
Registration newRegistration) {
// by saving the last delivered location state we are able to potentially delay the
// resulting provider request longer and save additional power
- newRegistration.initializeLastLocation(oldRegistration.getLastDeliveredLocation());
+ newRegistration.setLastDeliveredLocation(oldRegistration.getLastDeliveredLocation());
super.onRegistrationReplaced(key, oldRegistration, newRegistration);
}
@@ -2056,7 +2015,9 @@
setLastLocation(location, UserHandle.USER_ALL);
// attempt listener delivery
- deliverToListeners(registration -> registration.acceptLocationChange(location));
+ deliverToListeners(registration -> {
+ return registration.acceptLocationChange(location);
+ });
// notify passive provider
if (mPassiveManager != null) {
@@ -2194,8 +2155,7 @@
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
}
- @Nullable
- private Location getPermittedLocation(@Nullable Location fineLocation,
+ private @Nullable Location getPermittedLocation(@Nullable Location fineLocation,
@PermissionLevel int permissionLevel) {
switch (permissionLevel) {
case PERMISSION_FINE:
@@ -2250,10 +2210,10 @@
private static class LastLocation {
- @Nullable private Location mFineLocation;
- @Nullable private Location mCoarseLocation;
- @Nullable private Location mFineBypassLocation;
- @Nullable private Location mCoarseBypassLocation;
+ private @Nullable Location mFineLocation;
+ private @Nullable Location mCoarseLocation;
+ private @Nullable Location mFineBypassLocation;
+ private @Nullable Location mCoarseBypassLocation;
public void clearMock() {
if (mFineLocation != null && mFineLocation.isFromMockProvider()) {
@@ -2275,8 +2235,8 @@
mCoarseLocation = null;
}
- @Nullable
- public Location get(@PermissionLevel int permissionLevel, boolean ignoreLocationSettings) {
+ public @Nullable Location get(@PermissionLevel int permissionLevel,
+ boolean ignoreLocationSettings) {
switch (permissionLevel) {
case PERMISSION_FINE:
if (ignoreLocationSettings) {
@@ -2337,13 +2297,12 @@
private static class SingleUseCallback extends IRemoteCallback.Stub implements Runnable,
CancellationSignal.OnCancelListener {
- @Nullable
- public static SingleUseCallback wrap(@Nullable Runnable callback) {
+ public static @Nullable SingleUseCallback wrap(@Nullable Runnable callback) {
return callback == null ? null : new SingleUseCallback(callback);
}
@GuardedBy("this")
- @Nullable private Runnable mCallback;
+ private @Nullable Runnable mCallback;
private SingleUseCallback(Runnable callback) {
mCallback = Objects.requireNonNull(callback);
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index c91ee82..7a59cba 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -41,7 +41,6 @@
import android.util.ArraySet;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
import com.android.server.PendingIntentUtils;
import com.android.server.location.LocationPermissions;
import com.android.server.location.listeners.ListenerMultiplexer;
@@ -60,8 +59,8 @@
* Manages all geofences.
*/
public class GeofenceManager extends
- ListenerMultiplexer<GeofenceKey, PendingIntent, ListenerOperation<PendingIntent>,
- GeofenceManager.GeofenceRegistration, LocationRequest> implements
+ ListenerMultiplexer<GeofenceKey, PendingIntent, GeofenceManager.GeofenceRegistration,
+ LocationRequest> implements
LocationListener {
private static final String TAG = "GeofenceManager";
@@ -121,12 +120,10 @@
}
@Override
- protected ListenerOperation<PendingIntent> onActive() {
+ protected void onActive() {
Location location = getLastLocation();
if (location != null) {
- return onLocationChanged(location);
- } else {
- return null;
+ executeOperation(onLocationChanged(location));
}
}
@@ -304,7 +301,7 @@
final long identity = Binder.clearCallingIdentity();
try {
- addRegistration(new GeofenceKey(pendingIntent, geofence),
+ putRegistration(new GeofenceKey(pendingIntent, geofence),
new GeofenceRegistration(geofence, callerIdentity, pendingIntent));
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index ec48d4c..7592d22 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -32,7 +32,6 @@
import android.os.Process;
import android.util.ArraySet;
-import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.location.listeners.BinderListenerRegistration;
@@ -60,7 +59,7 @@
*/
public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInterface,
TMergedRegistration> extends
- ListenerMultiplexer<IBinder, TListener, ListenerOperation<TListener>,
+ ListenerMultiplexer<IBinder, TListener,
GnssListenerMultiplexer<TRequest, TListener, TMergedRegistration>
.GnssListenerRegistration, TMergedRegistration> {
@@ -231,7 +230,7 @@
TListener listener) {
final long identity = Binder.clearCallingIdentity();
try {
- addRegistration(listener.asBinder(),
+ putRegistration(listener.asBinder(),
createRegistration(request, callerIdentity, listener));
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 74284f3..4a3f94f 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -63,18 +63,16 @@
@Nullable
@Override
- protected ListenerOperation<IGnssMeasurementsListener> onActive() {
+ protected void onActive() {
mLocationAttributionHelper.reportHighPowerLocationStart(
getIdentity(), GNSS_MEASUREMENTS_BUCKET, getKey());
- return null;
}
@Nullable
@Override
- protected ListenerOperation<IGnssMeasurementsListener> onInactive() {
+ protected void onInactive() {
mLocationAttributionHelper.reportHighPowerLocationStop(
getIdentity(), GNSS_MEASUREMENTS_BUCKET, getKey());
- return null;
}
}
diff --git a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
index bc675ce..d6b179b 100644
--- a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
@@ -23,8 +23,6 @@
import android.os.RemoteException;
import android.util.Log;
-import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
-
/**
* A registration that works with IBinder keys, and registers a DeathListener to automatically
* remove the registration if the binder dies. The key for this registration must either be an
@@ -34,7 +32,7 @@
* @param <TListener> listener type
*/
public abstract class BinderListenerRegistration<TRequest, TListener> extends
- RemoteListenerRegistration<TRequest, TListener, ListenerOperation<TListener>> implements
+ RemoteListenerRegistration<TRequest, TListener> implements
Binder.DeathRecipient {
/**
diff --git a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
index 0318ffb..6b93616 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
@@ -75,13 +75,11 @@
*
* @param <TKey> key type
* @param <TListener> listener type
- * @param <TListenerOperation> listener operation type
* @param <TRegistration> registration type
* @param <TMergedRegistration> merged registration type
*/
public abstract class ListenerMultiplexer<TKey, TListener,
- TListenerOperation extends ListenerOperation<TListener>,
- TRegistration extends ListenerRegistration<TListener, TListenerOperation>,
+ TRegistration extends ListenerRegistration<TListener>,
TMergedRegistration> {
@GuardedBy("mRegistrations")
@@ -218,10 +216,26 @@
protected void onInactive() {}
/**
- * Adds a new registration with the given key. This method cannot be called to add a
- * registration re-entrantly.
+ * Puts a new registration with the given key, replacing any previous registration under the
+ * same key. This method cannot be called to put a registration re-entrantly.
*/
- protected final void addRegistration(@NonNull TKey key, @NonNull TRegistration registration) {
+ protected final void putRegistration(@NonNull TKey key, @NonNull TRegistration registration) {
+ replaceRegistration(key, key, registration);
+ }
+
+ /**
+ * Atomically removes the registration with the old key and adds a new registration with the
+ * given key. If there was a registration for the old key,
+ * {@link #onRegistrationReplaced(Object, ListenerRegistration, ListenerRegistration)} will be
+ * invoked for the new registration and key instead of
+ * {@link #onRegistrationAdded(Object, ListenerRegistration)}, even though they may not share
+ * the same key. The old key may be the same value as the new key, in which case this function
+ * is equivalent to {@link #putRegistration(Object, ListenerRegistration)}. This method cannot
+ * be called to add a registration re-entrantly.
+ */
+ protected final void replaceRegistration(@NonNull TKey oldKey, @NonNull TKey key,
+ @NonNull TRegistration registration) {
+ Objects.requireNonNull(oldKey);
Objects.requireNonNull(key);
Objects.requireNonNull(registration);
@@ -229,6 +243,9 @@
// adding listeners reentrantly is not supported
Preconditions.checkState(!mReentrancyGuard.isReentrant());
+ // new key may only have a prior registration if the oldKey is the same as the key
+ Preconditions.checkArgument(oldKey == key || !mRegistrations.containsKey(key));
+
// since adding a registration can invoke a variety of callbacks, we need to ensure
// those callbacks themselves do not re-enter, as this could lead to out-of-order
// callbacks. further, we buffer service updates since adding a registration may
@@ -241,9 +258,11 @@
boolean wasEmpty = mRegistrations.isEmpty();
TRegistration oldRegistration = null;
- int index = mRegistrations.indexOfKey(key);
+ int index = mRegistrations.indexOfKey(oldKey);
if (index >= 0) {
- oldRegistration = removeRegistration(index, false);
+ oldRegistration = removeRegistration(index, oldKey != key);
+ }
+ if (oldKey == key && index >= 0) {
mRegistrations.setValueAt(index, registration);
} else {
mRegistrations.put(key, registration);
@@ -316,7 +335,7 @@
* re-entrancy, and may be called to remove a registration re-entrantly.
*/
protected final void removeRegistration(@NonNull Object key,
- @NonNull ListenerRegistration<?, ?> registration) {
+ @NonNull ListenerRegistration<?> registration) {
synchronized (mRegistrations) {
int index = mRegistrations.indexOfKey(key);
if (index < 0) {
@@ -478,15 +497,9 @@
if (++mActiveRegistrationsCount == 1) {
onActive();
}
- TListenerOperation operation = registration.onActive();
- if (operation != null) {
- execute(registration, operation);
- }
+ registration.onActive();
} else {
- TListenerOperation operation = registration.onInactive();
- if (operation != null) {
- execute(registration, operation);
- }
+ registration.onInactive();
if (--mActiveRegistrationsCount == 0) {
onInactive();
}
@@ -502,16 +515,16 @@
* change the active state of the registration.
*/
protected final void deliverToListeners(
- @NonNull Function<TRegistration, TListenerOperation> function) {
+ @NonNull Function<TRegistration, ListenerOperation<TListener>> function) {
synchronized (mRegistrations) {
try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
final int size = mRegistrations.size();
for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (registration.isActive()) {
- TListenerOperation operation = function.apply(registration);
+ ListenerOperation<TListener> operation = function.apply(registration);
if (operation != null) {
- execute(registration, operation);
+ registration.executeOperation(operation);
}
}
}
@@ -526,14 +539,14 @@
* deliverToListeners(registration -> operation);
* </pre>
*/
- protected final void deliverToListeners(@NonNull TListenerOperation operation) {
+ protected final void deliverToListeners(@NonNull ListenerOperation<TListener> operation) {
synchronized (mRegistrations) {
try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
final int size = mRegistrations.size();
for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (registration.isActive()) {
- execute(registration, operation);
+ registration.executeOperation(operation);
}
}
}
@@ -545,10 +558,6 @@
onRegistrationActiveChanged(registration);
}
- private void execute(TRegistration registration, TListenerOperation operation) {
- registration.executeInternal(operation);
- }
-
/**
* Dumps debug information.
*/
@@ -606,7 +615,7 @@
@GuardedBy("mRegistrations")
private int mGuardCount;
@GuardedBy("mRegistrations")
- private @Nullable ArraySet<Entry<Object, ListenerRegistration<?, ?>>> mScheduledRemovals;
+ private @Nullable ArraySet<Entry<Object, ListenerRegistration<?>>> mScheduledRemovals;
ReentrancyGuard() {
mGuardCount = 0;
@@ -622,7 +631,7 @@
}
@GuardedBy("mRegistrations")
- void markForRemoval(Object key, ListenerRegistration<?, ?> registration) {
+ void markForRemoval(Object key, ListenerRegistration<?> registration) {
if (Build.IS_DEBUGGABLE) {
Preconditions.checkState(Thread.holdsLock(mRegistrations));
}
@@ -641,7 +650,7 @@
@Override
public void close() {
- ArraySet<Entry<Object, ListenerRegistration<?, ?>>> scheduledRemovals = null;
+ ArraySet<Entry<Object, ListenerRegistration<?>>> scheduledRemovals = null;
Preconditions.checkState(mGuardCount > 0);
if (--mGuardCount == 0) {
@@ -656,7 +665,7 @@
try (UpdateServiceBuffer ignored = mUpdateServiceBuffer.acquire()) {
final int size = scheduledRemovals.size();
for (int i = 0; i < size; i++) {
- Entry<Object, ListenerRegistration<?, ?>> entry = scheduledRemovals.valueAt(i);
+ Entry<Object, ListenerRegistration<?>> entry = scheduledRemovals.valueAt(i);
removeRegistration(entry.getKey(), entry.getValue());
}
}
diff --git a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
index d7ecbcb..fa21b3a 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
@@ -17,11 +17,9 @@
package com.android.server.location.listeners;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import com.android.internal.listeners.ListenerExecutor;
-import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -31,11 +29,8 @@
* request, and an executor responsible for listener invocations.
*
* @param <TListener> listener type
- * @param <TListenerOperation> listener operation type
*/
-public class ListenerRegistration<TListener,
- TListenerOperation extends ListenerOperation<TListener>> implements
- ListenerExecutor {
+public class ListenerRegistration<TListener> implements ListenerExecutor {
private final Executor mExecutor;
@@ -70,18 +65,14 @@
* returns a non-null operation, that operation will be invoked for the listener. Invoked
* while holding the owning multiplexer's internal lock.
*/
- protected @Nullable TListenerOperation onActive() {
- return null;
- }
+ protected void onActive() {}
/**
* May be overridden by subclasses. Invoked when registration becomes inactive. If this returns
* a non-null operation, that operation will be invoked for the listener. Invoked while holding
* the owning multiplexer's internal lock.
*/
- protected @Nullable TListenerOperation onInactive() {
- return null;
- }
+ protected void onInactive() {}
public final boolean isActive() {
return mActive;
@@ -114,27 +105,20 @@
protected void onListenerUnregister() {}
/**
- * May be overridden by subclasses, however should rarely be needed. Invoked whenever a listener
- * operation is submitted for execution, and allows the registration a chance to replace the
- * listener operation or perform related bookkeeping. There is no guarantee a listener operation
- * submitted or returned here will ever be invoked. Will always be invoked on the calling
- * thread.
- */
- protected TListenerOperation onExecuteOperation(@NonNull TListenerOperation operation) {
- return operation;
- }
-
- /**
* May be overridden by subclasses to handle listener operation failures. The default behavior
* is to further propagate any exceptions. Will always be invoked on the executor thread.
*/
- protected void onOperationFailure(TListenerOperation operation, Exception exception) {
- throw new AssertionError(exception);
+ protected void onOperationFailure(ListenerOperation<TListener> operation, Exception e) {
+ throw new AssertionError(e);
}
- final void executeInternal(@NonNull TListenerOperation operation) {
- executeSafely(mExecutor, () -> mListener,
- onExecuteOperation(Objects.requireNonNull(operation)), this::onOperationFailure);
+ /**
+ * Executes the given listener operation, invoking
+ * {@link #onOperationFailure(ListenerOperation, Exception)} in case the listener operation
+ * fails.
+ */
+ protected final void executeOperation(@Nullable ListenerOperation<TListener> operation) {
+ executeSafely(mExecutor, () -> mListener, operation, this::onOperationFailure);
}
@Override
diff --git a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
index e57b532..0aafb29 100644
--- a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
@@ -21,8 +21,6 @@
import android.location.util.identity.CallerIdentity;
import android.util.Log;
-import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
-
/**
* A registration that works with PendingIntent keys, and registers a CancelListener to
* automatically remove the registration if the PendingIntent is canceled. The key for this
@@ -32,8 +30,7 @@
* @param <TListener> listener type
*/
public abstract class PendingIntentListenerRegistration<TRequest, TListener> extends
- RemoteListenerRegistration<TRequest, TListener, ListenerOperation<TListener>> implements
- PendingIntent.CancelListener {
+ RemoteListenerRegistration<TRequest, TListener> implements PendingIntent.CancelListener {
/**
* Interface to allowed pending intent retrieval when keys are not themselves PendingIntents.
@@ -73,7 +70,7 @@
protected void onPendingIntentListenerUnregister() {}
@Override
- public void onOperationFailure(ListenerOperation<TListener> operation, Exception e) {
+ protected void onOperationFailure(ListenerOperation<TListener> operation, Exception e) {
if (e instanceof PendingIntent.CanceledException) {
Log.w(getOwner().getTag(), "registration " + this + " removed", e);
remove();
diff --git a/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java
index 242bf32..4eca577 100644
--- a/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java
@@ -24,7 +24,6 @@
import android.os.Process;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
import com.android.server.FgThread;
import java.util.Objects;
@@ -38,11 +37,9 @@
*
* @param <TRequest> request type
* @param <TListener> listener type
- * @param <TListenerOperation> listener operation type
*/
-public abstract class RemoteListenerRegistration<TRequest, TListener,
- TListenerOperation extends ListenerOperation<TListener>> extends
- RemovableListenerRegistration<TRequest, TListener, TListenerOperation> {
+public abstract class RemoteListenerRegistration<TRequest, TListener> extends
+ RemovableListenerRegistration<TRequest, TListener> {
@VisibleForTesting
public static final Executor IN_PROCESS_EXECUTOR = FgThread.getExecutor();
diff --git a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
index d3b5f66..618ff24 100644
--- a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
@@ -18,8 +18,6 @@
import android.annotation.Nullable;
-import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
-
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -29,11 +27,9 @@
*
* @param <TRequest> request type
* @param <TListener> listener type
- * @param <TListenerOperation> listener operation type
*/
-public abstract class RemovableListenerRegistration<TRequest, TListener,
- TListenerOperation extends ListenerOperation<TListener>> extends
- RequestListenerRegistration<TRequest, TListener, TListenerOperation> {
+public abstract class RemovableListenerRegistration<TRequest, TListener> extends
+ RequestListenerRegistration<TRequest, TListener> {
private volatile @Nullable Object mKey;
@@ -47,8 +43,7 @@
* with. Often this is easiest to accomplish by defining registration subclasses as non-static
* inner classes of the multiplexer they are to be used with.
*/
- protected abstract ListenerMultiplexer<?, ? super TListener, ?
- super TListenerOperation, ?, ?> getOwner();
+ protected abstract ListenerMultiplexer<?, ? super TListener, ?, ?> getOwner();
/**
* Returns the key associated with this registration. May not be invoked before
diff --git a/services/core/java/com/android/server/location/listeners/RequestListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RequestListenerRegistration.java
index d97abae..0c2fc91 100644
--- a/services/core/java/com/android/server/location/listeners/RequestListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/RequestListenerRegistration.java
@@ -16,8 +16,6 @@
package com.android.server.location.listeners;
-import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
-
import java.util.concurrent.Executor;
/**
@@ -25,11 +23,9 @@
*
* @param <TRequest> request type
* @param <TListener> listener type
- * @param <TListenerOperation> listener operation type
*/
-public class RequestListenerRegistration<TRequest, TListener,
- TListenerOperation extends ListenerOperation<TListener>> extends
- ListenerRegistration<TListener, TListenerOperation> {
+public class RequestListenerRegistration<TRequest, TListener> extends
+ ListenerRegistration<TListener> {
private final TRequest mRequest;
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 715e41c..5d0544b 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -17,6 +17,7 @@
package com.android.server.locksettings;
import static android.content.Context.USER_SERVICE;
+import static android.text.TextUtils.formatSimple;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.internal.widget.LockPatternUtils.USER_FRP;
@@ -527,7 +528,7 @@
protected String getSynthenticPasswordStateFilePathForUser(int userId, long handle,
String name) {
final File baseDir = getSyntheticPasswordDirectoryForUser(userId);
- final String baseName = String.format("%016x.%s", handle, name);
+ final String baseName = formatSimple("%016x.%s", handle, name);
return new File(baseDir, baseName).getAbsolutePath();
}
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 05f2808..3a262d6 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -95,11 +95,13 @@
mProfile = Objects.requireNonNull(profile);
final Intent configIntent = new Intent(ACTION_VPN_SETTINGS);
- mConfigIntent = PendingIntent.getActivity(mContext, 0, configIntent, 0);
+ mConfigIntent = PendingIntent.getActivity(mContext, 0 /* requestCode */, configIntent,
+ PendingIntent.FLAG_IMMUTABLE);
final Intent resetIntent = new Intent(ACTION_LOCKDOWN_RESET);
resetIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mResetIntent = PendingIntent.getBroadcast(mContext, 0, resetIntent, 0);
+ mResetIntent = PendingIntent.getBroadcast(mContext, 0 /* requestCode */, resetIntent,
+ PendingIntent.FLAG_IMMUTABLE);
}
/**
diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
index affdcea..dc9839c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
@@ -18,11 +18,13 @@
import android.app.Notification;
import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
import java.util.Set;
public interface NotificationManagerInternal {
NotificationChannel getNotificationChannel(String pkg, int uid, String channelId);
+ NotificationChannelGroup getNotificationChannelGroup(String pkg, int uid, String channelId);
void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid,
String tag, int id, Notification notification, int userId);
void cancelNotification(String pkg, String basePkg, int callingUid, int callingPid,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4d6b760f..8ea4326 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3381,7 +3381,7 @@
@Override
public void createConversationNotificationChannelForPackage(String pkg, int uid,
- String triggeringKey, NotificationChannel parentChannel, String conversationId) {
+ NotificationChannel parentChannel, String conversationId) {
enforceSystemOrSystemUI("only system can call this");
Preconditions.checkNotNull(parentChannel);
Preconditions.checkNotNull(conversationId);
@@ -5590,6 +5590,12 @@
}
@Override
+ public NotificationChannelGroup getNotificationChannelGroup(String pkg, int uid, String
+ channelId) {
+ return mPreferencesHelper.getGroupForChannel(pkg, uid, channelId);
+ }
+
+ @Override
public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
String tag, int id, Notification notification, int userId) {
enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
@@ -6091,14 +6097,15 @@
}
// bubble or inline reply that's immutable?
- if (n.getBubbleMetadata() != null
+ // TODO (b/171418004): renable after app outreach
+ /*if (n.getBubbleMetadata() != null
&& n.getBubbleMetadata().getIntent() != null
&& hasFlag(mAmi.getPendingIntentFlags(
n.getBubbleMetadata().getIntent().getTarget()),
PendingIntent.FLAG_IMMUTABLE)) {
throw new IllegalArgumentException(r.getKey() + " Not posted."
+ " PendingIntents attached to bubbles must be mutable");
- }
+ }*/
if (n.actions != null) {
for (Notification.Action action : n.actions) {
@@ -10077,24 +10084,27 @@
@Override
public boolean isActivityStartAllowed(int uid, String packageName) {
- boolean block = CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK, uid);
- if (block || mPackagesShown.add(packageName)) {
- mUiHandler.post(() ->
- Toast.makeText(getUiContext(),
- "Indirect activity start from "
- + packageName + ". "
- + "This will be blocked in S.\n"
- + "See go/s-trampolines.",
- Toast.LENGTH_LONG).show());
- }
- String message =
+ String toastMessage = "Indirect activity start from " + packageName;
+ String logcatMessage =
"Indirect notification activity start (trampoline) from " + packageName;
- if (block) {
- Slog.e(TAG, message + " blocked");
+
+ if (CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK, uid)) {
+ toast(toastMessage + " blocked.");
+ Slog.e(TAG, logcatMessage + " blocked");
return false;
+ } else {
+ if (mPackagesShown.add(packageName)) {
+ toast(toastMessage + ". This will be blocked in S.");
+ }
+ Slog.w(TAG, logcatMessage + ", this should be avoided for performance reasons");
+ return true;
}
- Slog.w(TAG, message + ", this should be avoided for performance reasons");
- return true;
+ }
+
+ private void toast(String message) {
+ mUiHandler.post(() ->
+ Toast.makeText(getUiContext(), message + "\nSee go/s-trampolines.",
+ Toast.LENGTH_LONG).show());
}
}
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index bdf98f4..77713a6 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1349,6 +1349,24 @@
return groups;
}
+ public NotificationChannelGroup getGroupForChannel(String pkg, int uid, String channelId) {
+ synchronized (mPackagePreferences) {
+ PackagePreferences p = getPackagePreferencesLocked(pkg, uid);
+ if (p != null) {
+ NotificationChannel nc = p.channels.get(channelId);
+ if (nc != null && !nc.isDeleted()) {
+ if (nc.getGroup() != null) {
+ NotificationChannelGroup group = p.groups.get(nc.getGroup());
+ if (group != null) {
+ return group;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
public ArrayList<ConversationChannelWrapper> getConversations(boolean onlyImportant) {
synchronized (mPackagePreferences) {
ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>();
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index b145e1e..b4347e1 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -15,6 +15,8 @@
*/
package com.android.server.notification;
+import static android.text.TextUtils.formatSimple;
+
import android.annotation.NonNull;
import android.app.NotificationManager;
import android.content.Context;
@@ -138,7 +140,7 @@
boolean isGroupSummary = record.getNotification().isGroupSummary();
record.setGlobalSortKey(
- String.format("crtcl=0x%04x:intrsv=%c:grnk=0x%04x:gsmry=%c:%s:rnk=0x%04x",
+ formatSimple("crtcl=0x%04x:intrsv=%c:grnk=0x%04x:gsmry=%c:%s:rnk=0x%04x",
record.getCriticality(),
record.isRecentlyIntrusive()
&& record.getImportance() > NotificationManager.IMPORTANCE_MIN
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 48ec9b4..e0b57e4 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -26,10 +26,8 @@
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.PackageInfo;
-import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
import android.os.Environment;
import android.os.ServiceManager;
import android.os.SystemProperties;
@@ -149,27 +147,6 @@
}
}
- // Returns the current battery level as a 0-100 integer.
- private int getBatteryLevel() {
- IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
- Intent intent = registerReceiver(null, filter);
- int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
- int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
- boolean present = intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
-
- if (!present) {
- // No battery, treat as if 100%, no possibility of draining battery.
- return 100;
- }
-
- if (level < 0 || scale <= 0) {
- // Battery data unavailable. This should never happen, so assume the worst.
- return 0;
- }
-
- return (100 * level / scale);
- }
-
private long getLowStorageThreshold(Context context) {
@SuppressWarnings("deprecation")
final long lowThreshold = StorageManager.from(context).getStorageLowBytes(mDataDir);
@@ -198,9 +175,8 @@
private void postBootUpdate(JobParameters jobParams, PackageManagerService pm,
ArraySet<String> pkgs) {
- // Load low battery threshold from the system config. This is a 0-100 integer.
- final int lowBatteryThreshold = getResources().getInteger(
- com.android.internal.R.integer.config_lowBatteryWarningLevel);
+ final BatteryManagerInternal batteryManagerInternal =
+ LocalServices.getService(BatteryManagerInternal.class);
final long lowThreshold = getLowStorageThreshold(this);
mAbortPostBootUpdate.set(false);
@@ -215,7 +191,7 @@
// Different job, which supersedes this one, is running.
break;
}
- if (getBatteryLevel() < lowBatteryThreshold) {
+ if (batteryManagerInternal.getBatteryLevelLow()) {
// Rather bail than completely drain the battery.
break;
}
diff --git a/services/core/java/com/android/server/pm/IncrementalStates.java b/services/core/java/com/android/server/pm/IncrementalStates.java
index 43f4a34..72803ac 100644
--- a/services/core/java/com/android/server/pm/IncrementalStates.java
+++ b/services/core/java/com/android/server/pm/IncrementalStates.java
@@ -105,8 +105,8 @@
if (!mStartableState.isStartable()) {
mStartableState.adoptNewStartableStateLocked(true);
}
- if (mLoadingState.isLoading() != isIncremental) {
- mLoadingState.adoptNewLoadingStateLocked(isIncremental);
+ if (!isIncremental) {
+ updateProgressLocked(1);
}
}
mHandler.post(PooledLambda.obtainRunnable(
@@ -119,6 +119,33 @@
}
}
+ /**
+ * Change the startable state if the app has crashed or ANR'd during loading.
+ * If the app is not loading (i.e., fully loaded), this event doesn't change startable state.
+ */
+ public void onCrashOrAnr() {
+ if (DEBUG) {
+ Slog.i(TAG, "received package crash or ANR event");
+ }
+ final boolean startableStateChanged;
+ synchronized (mLock) {
+ if (mStartableState.isStartable() && mLoadingState.isLoading()) {
+ // Changing from startable -> unstartable only if app is still loading.
+ mStartableState.adoptNewStartableStateLocked(false);
+ startableStateChanged = true;
+ } else {
+ // If the app is fully loaded, the crash or ANR is caused by the app itself, so
+ // we do not change the startable state.
+ startableStateChanged = false;
+ }
+ }
+ if (startableStateChanged) {
+ mHandler.post(PooledLambda.obtainRunnable(
+ IncrementalStates::reportStartableState,
+ IncrementalStates.this).recycleOnUse());
+ }
+ }
+
private void reportStartableState() {
final Callback callback;
final boolean startable;
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 9bb6f78..b679c0f 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.pm.LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS;
@@ -32,6 +33,7 @@
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
import android.app.usage.UsageStatsManagerInternal;
+import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -44,6 +46,8 @@
import android.content.pm.IPackageInstallerCallback;
import android.content.pm.IPackageManager;
import android.content.pm.IShortcutChangeCallback;
+import android.content.pm.IncrementalStatesInfo;
+import android.content.pm.LauncherActivityInfoInternal;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageInfo;
@@ -369,18 +373,13 @@
}
}
- private ResolveInfo getHiddenAppActivityInfo(String packageName, int callingUid,
- UserHandle user) {
+ private LauncherActivityInfoInternal getHiddenAppActivityInfo(String packageName,
+ int callingUid, UserHandle user) {
Intent intent = new Intent();
intent.setComponent(new ComponentName(packageName,
PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME));
- final PackageManagerInternal pmInt =
- LocalServices.getService(PackageManagerInternal.class);
- List<ResolveInfo> apps = pmInt.queryIntentActivities(intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- callingUid, user.getIdentifier());
+ final List<LauncherActivityInfoInternal> apps = queryIntentLauncherActivities(intent,
+ callingUid, user);
if (apps.size() > 0) {
return apps.get(0);
}
@@ -399,14 +398,14 @@
}
@Override
- public ParceledListSlice<ResolveInfo> getLauncherActivities(String callingPackage,
- String packageName, UserHandle user) throws RemoteException {
- ParceledListSlice<ResolveInfo> launcherActivities = queryActivitiesForUser(
- callingPackage,
- new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_LAUNCHER)
- .setPackage(packageName),
- user);
+ public ParceledListSlice<LauncherActivityInfoInternal> getLauncherActivities(
+ String callingPackage, String packageName, UserHandle user) throws RemoteException {
+ ParceledListSlice<LauncherActivityInfoInternal> launcherActivities =
+ queryActivitiesForUser(callingPackage,
+ new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_LAUNCHER)
+ .setPackage(packageName),
+ user);
if (Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 1) == 0) {
return launcherActivities;
@@ -428,7 +427,8 @@
return launcherActivities;
}
- final ArrayList<ResolveInfo> result = new ArrayList<>(launcherActivities.getList());
+ final ArrayList<LauncherActivityInfoInternal> result = new ArrayList<>(
+ launcherActivities.getList());
final PackageManagerInternal pmInt =
LocalServices.getService(PackageManagerInternal.class);
if (packageName != null) {
@@ -440,7 +440,8 @@
ApplicationInfo appInfo = pmInt.getApplicationInfo(packageName, /*flags*/ 0,
callingUid, user.getIdentifier());
if (shouldShowSyntheticActivity(user, appInfo)) {
- ResolveInfo info = getHiddenAppActivityInfo(packageName, callingUid, user);
+ LauncherActivityInfoInternal info = getHiddenAppActivityInfo(packageName,
+ callingUid, user);
if (info != null) {
result.add(info);
}
@@ -448,8 +449,8 @@
return new ParceledListSlice<>(result);
}
final HashSet<String> visiblePackages = new HashSet<>();
- for (ResolveInfo info : result) {
- visiblePackages.add(info.activityInfo.packageName);
+ for (LauncherActivityInfoInternal info : result) {
+ visiblePackages.add(info.getActivityInfo().packageName);
}
List<ApplicationInfo> installedPackages = pmInt.getInstalledApplications(0,
user.getIdentifier(), callingUid);
@@ -458,8 +459,8 @@
if (!shouldShowSyntheticActivity(user, applicationInfo)) {
continue;
}
- ResolveInfo info = getHiddenAppActivityInfo(applicationInfo.packageName,
- callingUid, user);
+ LauncherActivityInfoInternal info = getHiddenAppActivityInfo(
+ applicationInfo.packageName, callingUid, user);
if (info != null) {
result.add(info);
}
@@ -533,7 +534,7 @@
}
@Override
- public ActivityInfo resolveActivity(
+ public LauncherActivityInfoInternal resolveLauncherActivityInternal(
String callingPackage, ComponentName component, UserHandle user)
throws RemoteException {
if (!canAccessProfile(user.getIdentifier(), "Cannot resolve activity")) {
@@ -545,10 +546,24 @@
try {
final PackageManagerInternal pmInt =
LocalServices.getService(PackageManagerInternal.class);
- return pmInt.getActivityInfo(component,
+ final ActivityInfo activityInfo = pmInt.getActivityInfo(component,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
callingUid, user.getIdentifier());
+ if (activityInfo == null) {
+ return null;
+ }
+ if (component == null || component.getPackageName() == null) {
+ // should not happen
+ return null;
+ }
+ final IncrementalStatesInfo incrementalStatesInfo = pmInt.getIncrementalStatesInfo(
+ component.getPackageName(), callingUid, user.getIdentifier());
+ if (incrementalStatesInfo == null) {
+ // package does not exist; should not happen
+ return null;
+ }
+ return new LauncherActivityInfoInternal(activityInfo, incrementalStatesInfo);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -562,28 +577,51 @@
new Intent(Intent.ACTION_CREATE_SHORTCUT).setPackage(packageName), user);
}
- private ParceledListSlice<ResolveInfo> queryActivitiesForUser(String callingPackage,
- Intent intent, UserHandle user) {
+ private ParceledListSlice<LauncherActivityInfoInternal> queryActivitiesForUser(
+ String callingPackage, Intent intent, UserHandle user) {
if (!canAccessProfile(user.getIdentifier(), "Cannot retrieve activities")) {
return null;
}
-
final int callingUid = injectBinderCallingUid();
long ident = injectClearCallingIdentity();
try {
- final PackageManagerInternal pmInt =
- LocalServices.getService(PackageManagerInternal.class);
- List<ResolveInfo> apps = pmInt.queryIntentActivities(intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- callingUid, user.getIdentifier());
- return new ParceledListSlice<>(apps);
+ return new ParceledListSlice<>(queryIntentLauncherActivities(intent, callingUid,
+ user));
} finally {
injectRestoreCallingIdentity(ident);
}
}
+ private List<LauncherActivityInfoInternal> queryIntentLauncherActivities(
+ Intent intent, int callingUid, UserHandle user) {
+ final PackageManagerInternal pmInt =
+ LocalServices.getService(PackageManagerInternal.class);
+ List<ResolveInfo> apps = pmInt.queryIntentActivities(intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ callingUid, user.getIdentifier());
+ final int numResolveInfos = apps.size();
+ List<LauncherActivityInfoInternal> results = new ArrayList<>();
+ for (int i = 0; i < numResolveInfos; i++) {
+ final ResolveInfo ri = apps.get(i);
+ final String packageName = ri.activityInfo.packageName;
+ if (packageName == null) {
+ // should not happen
+ continue;
+ }
+ final IncrementalStatesInfo incrementalStatesInfo = pmInt.getIncrementalStatesInfo(
+ packageName, callingUid, user.getIdentifier());
+ if (incrementalStatesInfo == null) {
+ // package doesn't exist any more; should not happen
+ continue;
+ }
+ results.add(new LauncherActivityInfoInternal(ri.activityInfo,
+ incrementalStatesInfo));
+ }
+ return results;
+ }
+
@Override
public IntentSender getShortcutConfigActivityIntent(String callingPackage,
ComponentName component, UserHandle user) throws RemoteException {
@@ -982,6 +1020,32 @@
}
@Override
+ public PendingIntent getActivityLaunchIntent(ComponentName component, Bundle opts,
+ UserHandle user) {
+ if (!canAccessProfile(user.getIdentifier(), "Cannot start activity")) {
+ throw new ActivityNotFoundException("Activity could not be found");
+ }
+
+ final Intent launchIntent = getMainActivityLaunchIntent(component, user);
+ if (launchIntent == null) {
+ throw new SecurityException("Attempt to launch activity without "
+ + " category Intent.CATEGORY_LAUNCHER " + component);
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ // If we reach here, we've verified that the caller has access to the profile and
+ // is launching an exported activity with CATEGORY_LAUNCHER so we can clear the
+ // calling identity to mirror the startActivityAsUser() call which does not validate
+ // the calling user
+ return PendingIntent.getActivityAsUser(mContext, 0 /* requestCode */, launchIntent,
+ FLAG_IMMUTABLE, opts, user);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public void startActivityAsUser(IApplicationThread caller, String callingPackage,
String callingFeatureId, ComponentName component, Rect sourceBounds,
Bundle opts, UserHandle user) throws RemoteException {
@@ -989,9 +1053,24 @@
return;
}
+ Intent launchIntent = getMainActivityLaunchIntent(component, user);
+ if (launchIntent == null) {
+ throw new SecurityException("Attempt to launch activity without "
+ + " category Intent.CATEGORY_LAUNCHER " + component);
+ }
+ launchIntent.setSourceBounds(sourceBounds);
+
+ mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage,
+ callingFeatureId, launchIntent, /* resultTo= */ null,
+ Intent.FLAG_ACTIVITY_NEW_TASK, opts, user.getIdentifier());
+ }
+
+ /**
+ * Returns the main activity launch intent for the given component package.
+ */
+ private Intent getMainActivityLaunchIntent(ComponentName component, UserHandle user) {
Intent launchIntent = new Intent(Intent.ACTION_MAIN);
launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- launchIntent.setSourceBounds(sourceBounds);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
launchIntent.setPackage(component.getPackageName());
@@ -1030,15 +1109,12 @@
}
}
if (!canLaunch) {
- throw new SecurityException("Attempt to launch activity without "
- + " category Intent.CATEGORY_LAUNCHER " + component);
+ return null;
}
} finally {
Binder.restoreCallingIdentity(ident);
}
- mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage,
- callingFeatureId, launchIntent, /* resultTo= */ null,
- Intent.FLAG_ACTIVITY_NEW_TASK, opts, user.getIdentifier());
+ return launchIntent;
}
@Override
@@ -1238,7 +1314,10 @@
} finally {
mListeners.finishBroadcast();
}
-
+ PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+ pmi.registerInstalledLoadingProgressCallback(packageName,
+ new PackageLoadingProgressCallback(packageName, user),
+ user.getIdentifier());
super.onPackageAdded(packageName, uid);
}
@@ -1266,6 +1345,11 @@
@Override
public void onPackageModified(String packageName) {
+ onPackageChanged(packageName);
+ super.onPackageModified(packageName);
+ }
+
+ private void onPackageChanged(String packageName) {
UserHandle user = new UserHandle(getChangingUserId());
final int n = mListeners.beginBroadcast();
try {
@@ -1282,8 +1366,6 @@
} finally {
mListeners.finishBroadcast();
}
-
- super.onPackageModified(packageName);
}
@Override
@@ -1435,6 +1517,7 @@
} catch (RemoteException re) {
Slog.d(TAG, "Callback failed ", re);
}
+
}
} catch (RuntimeException e) {
// When the user is locked we get IllegalState, so just catch all.
@@ -1443,6 +1526,12 @@
mListeners.finishBroadcast();
}
}
+
+ @Override
+ public void onPackageStateChanged(String packageName, int uid) {
+ onPackageChanged(packageName);
+ super.onPackageStateChanged(packageName, uid);
+ }
}
class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> {
@@ -1451,5 +1540,38 @@
checkCallbackCount();
}
}
+
+ class PackageLoadingProgressCallback extends
+ PackageManagerInternal.InstalledLoadingProgressCallback {
+ private String mPackageName;
+ private UserHandle mUser;
+
+ PackageLoadingProgressCallback(String packageName, UserHandle user) {
+ super(mCallbackHandler);
+ mPackageName = packageName;
+ mUser = user;
+ }
+
+ @Override
+ public void onLoadingProgressChanged(float progress) {
+ final int n = mListeners.beginBroadcast();
+ try {
+ for (int i = 0; i < n; i++) {
+ IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+ BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
+ if (!isEnabledProfileOf(cookie.user, mUser, "onLoadingProgressChanged")) {
+ continue;
+ }
+ try {
+ listener.onPackageProgressChanged(mUser, mPackageName, progress);
+ } catch (RemoteException re) {
+ Slog.d(TAG, "Callback failed ", re);
+ }
+ }
+ } finally {
+ mListeners.finishBroadcast();
+ }
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 2e1543b..c282b99 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -592,10 +592,9 @@
params.installFlags &= ~PackageManager.INSTALL_REQUEST_DOWNGRADE;
}
- if (callingUid != Process.SYSTEM_UID
- && (params.installFlags & ADB_DEV_MODE) != ADB_DEV_MODE) {
- // Only system_server or tools under specific conditions (test app installed
- // through ADB, and verification disabled flag specified) can disable verification.
+ if ((params.installFlags & ADB_DEV_MODE) != ADB_DEV_MODE) {
+ // Only tools under specific conditions (test app installed through ADB, and
+ // verification disabled flag specified) can disable verification.
params.installFlags &= ~PackageManager.INSTALL_DISABLE_VERIFICATION;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3f16abf..7eb4fd9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2006 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
@@ -47,8 +46,10 @@
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER;
import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
+import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_PERMISSION_GROUP;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
+import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP;
import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
@@ -180,6 +181,7 @@
import android.content.pm.IPackageManagerNative;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
+import android.content.pm.IncrementalStatesInfo;
import android.content.pm.InstallSourceInfo;
import android.content.pm.InstantAppInfo;
import android.content.pm.InstantAppRequest;
@@ -220,6 +222,7 @@
import android.content.pm.Signature;
import android.content.pm.SigningInfo;
import android.content.pm.SuspendDialogInfo;
+import android.content.pm.TestUtilityService;
import android.content.pm.UserInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VerifierInfo;
@@ -233,6 +236,7 @@
import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
import android.content.pm.parsing.component.ParsedProcess;
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
@@ -333,6 +337,7 @@
import com.android.internal.os.Zygote;
import com.android.internal.telephony.CarrierAppUtils;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
@@ -476,7 +481,7 @@
* </pre>
*/
public class PackageManagerService extends IPackageManager.Stub
- implements PackageSender {
+ implements PackageSender, TestUtilityService {
static final String TAG = "PackageManager";
public static final boolean DEBUG_SETTINGS = false;
static final boolean DEBUG_PREFERRED = false;
@@ -816,6 +821,7 @@
boolean mPromoteSystemApps;
private final PackageManagerInternal mPmInternal;
+ private final TestUtilityService mTestUtilityService;
@GuardedBy("mLock")
@@ -1189,6 +1195,7 @@
public IPermissionManager permissionManagerService;
public PendingPackageBroadcasts pendingPackageBroadcasts;
public PackageManagerInternal pmInternal;
+ public TestUtilityService testUtilityService;
public ProcessLoggingHandler processLoggingHandler;
public ProtectedPackages protectedPackages;
public @NonNull String requiredInstallerPackage;
@@ -2953,6 +2960,7 @@
mPendingBroadcasts = testParams.pendingPackageBroadcasts;
mPermissionManagerService = testParams.permissionManagerService;
mPmInternal = testParams.pmInternal;
+ mTestUtilityService = testParams.testUtilityService;
mProcessLoggingHandler = testParams.processLoggingHandler;
mProtectedPackages = testParams.protectedPackages;
mSeparateProcesses = testParams.separateProcesses;
@@ -3023,6 +3031,8 @@
// Expose private service for system components to use.
mPmInternal = new PackageManagerInternalImpl();
+ LocalServices.addService(TestUtilityService.class, this);
+ mTestUtilityService = LocalServices.getService(TestUtilityService.class);
LocalServices.addService(PackageManagerInternal.class, mPmInternal);
mUserManager = injector.getUserManagerService();
mComponentResolver = injector.getComponentResolver();
@@ -12684,12 +12694,17 @@
mPermissionManager.addAllPermissionGroups(pkg, chatty);
}
+ // If a permission has had its defining app changed, or it has had its protection
+ // upgraded, we need to revoke apps that hold it
+ final List<String> permissionsWithChangedDefinition;
// Don't allow ephemeral applications to define new permissions.
if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
+ permissionsWithChangedDefinition = null;
Slog.w(TAG, "Permissions from package " + pkg.getPackageName()
+ " ignored: instant apps cannot define new permissions.");
} else {
- mPermissionManager.addAllPermissions(pkg, chatty);
+ permissionsWithChangedDefinition =
+ mPermissionManager.addAllPermissions(pkg, chatty);
}
int collectionSize = ArrayUtils.size(pkg.getInstrumentations());
@@ -12718,7 +12733,10 @@
}
}
- if (oldPkg != null) {
+ boolean hasOldPkg = oldPkg != null;
+ boolean hasPermissionDefinitionChanges =
+ !CollectionUtils.isEmpty(permissionsWithChangedDefinition);
+ if (hasOldPkg || hasPermissionDefinitionChanges) {
// We need to call revokeRuntimePermissionsIfGroupChanged async as permission
// revoke callbacks from this method might need to kill apps which need the
// mPackages lock on a different thread. This would dead lock.
@@ -12729,9 +12747,16 @@
// won't be granted yet, hence new packages are no problem.
final ArrayList<String> allPackageNames = new ArrayList<>(mPackages.keySet());
- AsyncTask.execute(() ->
+ AsyncTask.execute(() -> {
+ if (hasOldPkg) {
mPermissionManager.revokeRuntimePermissionsIfGroupChanged(pkg, oldPkg,
- allPackageNames));
+ allPackageNames);
+ }
+ if (hasPermissionDefinitionChanges) {
+ mPermissionManager.revokeRuntimePermissionsIfPermissionDefinitionChanged(
+ permissionsWithChangedDefinition, allPackageNames);
+ }
+ });
}
}
@@ -14320,10 +14345,6 @@
Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0;
}
- if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) {
- return false;
- }
-
// only when not installed from ADB, skip verification for instant apps when
// the installer and verifier are the same.
if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
@@ -17617,6 +17638,49 @@
}
}
+ private boolean doesSignatureMatchForPermissions(@NonNull String sourcePackageName,
+ @NonNull ParsedPackage parsedPackage, int scanFlags) {
+ // If the defining package is signed with our cert, it's okay. This
+ // also includes the "updating the same package" case, of course.
+ // "updating same package" could also involve key-rotation.
+
+ final PackageSetting sourcePackageSetting;
+ synchronized (mLock) {
+ sourcePackageSetting = mSettings.getPackageLPr(sourcePackageName);
+ }
+
+ final SigningDetails sourceSigningDetails = (sourcePackageSetting == null
+ ? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails());
+ final KeySetManagerService ksms = mSettings.mKeySetManagerService;
+ if (sourcePackageName.equals(parsedPackage.getPackageName())
+ && (ksms.shouldCheckUpgradeKeySetLocked(
+ sourcePackageSetting, scanFlags))) {
+ return ksms.checkUpgradeKeySetLocked(sourcePackageSetting, parsedPackage);
+ } else {
+
+ // in the event of signing certificate rotation, we need to see if the
+ // package's certificate has rotated from the current one, or if it is an
+ // older certificate with which the current is ok with sharing permissions
+ if (sourceSigningDetails.checkCapability(
+ parsedPackage.getSigningDetails(),
+ PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+ return true;
+ } else if (parsedPackage.getSigningDetails().checkCapability(
+ sourceSigningDetails,
+ PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
+ // the scanned package checks out, has signing certificate rotation
+ // history, and is newer; bring it over
+ synchronized (mLock) {
+ sourcePackageSetting.signatures.mSigningDetails =
+ parsedPackage.getSigningDetails();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
@GuardedBy("mInstallLock")
private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
throws PrepareFailure {
@@ -17775,6 +17839,15 @@
}
}
+ /*
+ * Cannot properly check CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS using CompatChanges
+ * as this only works for packages that are installed
+ *
+ * TODO: Move logic for permission group compatibility into PermissionManagerService
+ */
+ boolean cannotInstallWithBadPermissionGroups =
+ parsedPackage.getTargetSdkVersion() >= Build.VERSION_CODES.S;
+
PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
@@ -17826,8 +17899,33 @@
res.origUsers = ps.queryInstalledUsers(mUserManager.getUserIds(), true);
}
+ final int numGroups = ArrayUtils.size(parsedPackage.getPermissionGroups());
+ for (int groupNum = 0; groupNum < numGroups; groupNum++) {
+ final ParsedPermissionGroup group =
+ parsedPackage.getPermissionGroups().get(groupNum);
+ final PermissionGroupInfo sourceGroup = getPermissionGroupInfo(group.getName(), 0);
- int N = ArrayUtils.size(parsedPackage.getPermissions());
+ if (sourceGroup != null && cannotInstallWithBadPermissionGroups) {
+ final String sourcePackageName = sourceGroup.packageName;
+
+ if ((replace || !parsedPackage.getPackageName().equals(sourcePackageName))
+ && !doesSignatureMatchForPermissions(sourcePackageName, parsedPackage,
+ scanFlags)) {
+ EventLog.writeEvent(0x534e4554, "146211400", -1,
+ parsedPackage.getPackageName());
+
+ throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP,
+ "Package "
+ + parsedPackage.getPackageName()
+ + " attempting to redeclare permission group "
+ + group.getName() + " already owned by "
+ + sourcePackageName);
+ }
+ }
+ }
+
+ // TODO: Move logic for checking permission compatibility into PermissionManagerService
+ final int N = ArrayUtils.size(parsedPackage.getPermissions());
for (int i = N - 1; i >= 0; i--) {
final ParsedPermission perm = parsedPackage.getPermissions().get(i);
final BasePermission bp = mPermissionManager.getPermissionTEMP(perm.getName());
@@ -17843,46 +17941,10 @@
// Check whether the newly-scanned package wants to define an already-defined perm
if (bp != null) {
- // If the defining package is signed with our cert, it's okay. This
- // also includes the "updating the same package" case, of course.
- // "updating same package" could also involve key-rotation.
- final boolean sigsOk;
final String sourcePackageName = bp.getPackageName();
- final PackageSetting sourcePackageSetting;
- synchronized (mLock) {
- sourcePackageSetting = mSettings.getPackageLPr(sourcePackageName);
- }
- final SigningDetails sourceSigningDetails = (sourcePackageSetting == null
- ? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails());
- final KeySetManagerService ksms = mSettings.mKeySetManagerService;
- if (sourcePackageName.equals(parsedPackage.getPackageName())
- && (ksms.shouldCheckUpgradeKeySetLocked(
- sourcePackageSetting, scanFlags))) {
- sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, parsedPackage);
- } else {
- // in the event of signing certificate rotation, we need to see if the
- // package's certificate has rotated from the current one, or if it is an
- // older certificate with which the current is ok with sharing permissions
- if (sourceSigningDetails.checkCapability(
- parsedPackage.getSigningDetails(),
- PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
- sigsOk = true;
- } else if (parsedPackage.getSigningDetails().checkCapability(
- sourceSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
- // the scanned package checks out, has signing certificate rotation
- // history, and is newer; bring it over
- synchronized (mLock) {
- sourcePackageSetting.signatures.mSigningDetails =
- parsedPackage.getSigningDetails();
- }
- sigsOk = true;
- } else {
- sigsOk = false;
- }
- }
- if (!sigsOk) {
+ if (!doesSignatureMatchForPermissions(sourcePackageName, parsedPackage,
+ scanFlags)) {
// If the owning package is the system itself, we log but allow
// install to proceed; we fail the install on all other permission
// redefinitions.
@@ -17917,6 +17979,52 @@
}
}
}
+
+ if (perm.getGroup() != null && cannotInstallWithBadPermissionGroups) {
+ boolean isPermGroupDefinedByPackage = false;
+ for (int groupNum = 0; groupNum < numGroups; groupNum++) {
+ if (parsedPackage.getPermissionGroups().get(groupNum).getName()
+ .equals(perm.getGroup())) {
+ isPermGroupDefinedByPackage = true;
+ break;
+ }
+ }
+
+ if (!isPermGroupDefinedByPackage) {
+ final PermissionGroupInfo sourceGroup =
+ getPermissionGroupInfo(perm.getGroup(), 0);
+
+ if (sourceGroup == null) {
+ EventLog.writeEvent(0x534e4554, "146211400", -1,
+ parsedPackage.getPackageName());
+
+ throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP,
+ "Package "
+ + parsedPackage.getPackageName()
+ + " attempting to declare permission "
+ + perm.getName() + " in non-existing group "
+ + perm.getGroup());
+ } else {
+ String groupSourcePackageName = sourceGroup.packageName;
+
+ if (!PLATFORM_PACKAGE_NAME.equals(groupSourcePackageName)
+ && !doesSignatureMatchForPermissions(groupSourcePackageName,
+ parsedPackage, scanFlags)) {
+ EventLog.writeEvent(0x534e4554, "146211400", -1,
+ parsedPackage.getPackageName());
+
+ throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP,
+ "Package "
+ + parsedPackage.getPackageName()
+ + " attempting to declare permission "
+ + perm.getName() + " in group "
+ + perm.getGroup() + " owned by package "
+ + groupSourcePackageName
+ + " with incompatible certificate");
+ }
+ }
+ }
+ }
}
}
@@ -19120,7 +19228,7 @@
extras.putInt(Intent.EXTRA_UID, removedUid);
extras.putBoolean(Intent.EXTRA_DATA_REMOVED, dataRemoved);
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp);
- extras.putBoolean(Intent.EXTRA_REMOVED_BY_SYSTEM, removedBySystem);
+ extras.putBoolean(Intent.EXTRA_USER_INITIATED, !removedBySystem);
if (isUpdate || isRemovedPackageSystemUpdate) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
@@ -22328,11 +22436,8 @@
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
ipw.println("Known Packages:");
ipw.increaseIndent();
- for (int i = 0; i < LAST_KNOWN_PACKAGE; i++) {
+ for (int i = 0; i <= LAST_KNOWN_PACKAGE; i++) {
final String knownPackage = mPmInternal.knownPackageToString(i);
- if ("Unknown".equals(knownPackage)) {
- continue;
- }
ipw.print(knownPackage);
ipw.println(":");
final String[] pkgNames = mPmInternal.getKnownPackageNames(i,
@@ -25749,8 +25854,34 @@
return mIncrementalManager.unregisterLoadingProgressCallback(ps.getPathString(),
(IPackageLoadingProgressCallback) callback.getBinder());
}
+
+ @Override
+ public IncrementalStatesInfo getIncrementalStatesInfo(
+ @NonNull String packageName, int filterCallingUid, int userId) {
+ final PackageSetting ps = getPackageSettingForUser(packageName, filterCallingUid,
+ userId);
+ if (ps == null) {
+ return null;
+ }
+ return ps.getIncrementalStates();
+ }
+
+ @Override
+ public void notifyPackageCrashOrAnr(@NonNull String packageName) {
+ final PackageSetting ps;
+ synchronized (mLock) {
+ ps = mSettings.mPackages.get(packageName);
+ if (ps == null) {
+ Slog.w(TAG, "Failed notifyPackageCrash. Package " + packageName
+ + " is not installed");
+ return;
+ }
+ }
+ ps.setStatesOnCrashOrAnr();
+ }
}
+
@GuardedBy("mLock")
private SparseArray<String> getAppsWithSharedUserIdsLocked() {
final SparseArray<String> sharedUserIds = new SparseArray<>();
@@ -26241,9 +26372,39 @@
}
@Override
- public void holdLock(int durationMs) {
+ public IBinder getHoldLockToken() {
+ if (!Build.IS_DEBUGGABLE) {
+ throw new SecurityException("getHoldLockToken requires a debuggable build");
+ }
+
mContext.enforceCallingPermission(
- Manifest.permission.INJECT_EVENTS, "holdLock requires shell identity");
+ Manifest.permission.INJECT_EVENTS,
+ "getHoldLockToken requires INJECT_EVENTS permission");
+
+ final Binder token = new Binder();
+ token.attachInterface(this, "holdLock:" + Binder.getCallingUid());
+ return token;
+ }
+
+ @Override
+ public void verifyHoldLockToken(IBinder token) {
+ if (!Build.IS_DEBUGGABLE) {
+ throw new SecurityException("holdLock requires a debuggable build");
+ }
+
+ if (token == null) {
+ throw new SecurityException("null holdLockToken");
+ }
+
+ if (token.queryLocalInterface("holdLock:" + Binder.getCallingUid()) != this) {
+ throw new SecurityException("Invalid holdLock() token");
+ }
+ }
+
+ @Override
+ public void holdLock(IBinder token, int durationMs) {
+ mTestUtilityService.verifyHoldLockToken(token);
+
synchronized (mLock) {
SystemClock.sleep(durationMs);
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index be7c7c6..ac76cf7 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -772,6 +772,14 @@
}
/**
+ * Called to indicate that the running app has crashed or ANR'd. This might change the startable
+ * state of the package, depending on whether the package is fully loaded.
+ */
+ public void setStatesOnCrashOrAnr() {
+ incrementalStates.onCrashOrAnr();
+ }
+
+ /**
* Called to set the callback to listen for startable state changes.
*/
public void setIncrementalStatesCallback(IncrementalStates.Callback callback) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 96f9982..e471ac6 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -3907,10 +3907,11 @@
final long start = getStatStartTime();
final long token = injectClearCallingIdentity();
try {
- return mContext.getPackageManager().getResourcesForApplicationAsUser(
- packageName, userId);
+ return mContext.createContextAsUser(UserHandle.of(userId), /* flags */ 0)
+ .getPackageManager().getResourcesForApplication(packageName);
} catch (NameNotFoundException e) {
- Slog.e(TAG, "Resources for package " + packageName + " not found");
+ Slog.e(TAG, "Resources of package " + packageName + " for user " + userId
+ + " not found");
return null;
} finally {
injectRestoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 667414e..155d716 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -16,16 +16,6 @@
package com.android.server.pm.permission;
-import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
-import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
-import static android.content.pm.PermissionInfo.PROTECTION_NORMAL;
-import static android.content.pm.PermissionInfo.PROTECTION_SIGNATURE;
-import static android.content.pm.PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM;
-
-import static com.android.server.pm.Settings.ATTR_NAME;
-import static com.android.server.pm.Settings.ATTR_PACKAGE;
-import static com.android.server.pm.Settings.TAG_ITEM;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -58,42 +48,40 @@
import java.util.Set;
public final class BasePermission {
- static final String TAG = "PackageManager";
+ private static final String TAG = "PackageManager";
- public static final int TYPE_NORMAL = 0;
- public static final int TYPE_BUILTIN = 1;
+ public static final int TYPE_MANIFEST = 0;
+ public static final int TYPE_CONFIG = 1;
public static final int TYPE_DYNAMIC = 2;
- @IntDef(value = {
- TYPE_NORMAL,
- TYPE_BUILTIN,
- TYPE_DYNAMIC,
+ @IntDef({
+ TYPE_MANIFEST,
+ TYPE_CONFIG,
+ TYPE_DYNAMIC,
})
@Retention(RetentionPolicy.SOURCE)
public @interface PermissionType {}
- @IntDef(value = {
- PROTECTION_DANGEROUS,
- PROTECTION_NORMAL,
- PROTECTION_SIGNATURE,
- PROTECTION_SIGNATURE_OR_SYSTEM,
+ @IntDef({
+ PermissionInfo.PROTECTION_DANGEROUS,
+ PermissionInfo.PROTECTION_NORMAL,
+ PermissionInfo.PROTECTION_SIGNATURE,
+ PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ProtectionLevel {}
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_PACKAGE = "package";
+ private static final String TAG_ITEM = "item";
+
+ private boolean mPermissionDefinitionChanged;
+
@NonNull
- private final String mName;
-
- private final @PermissionType int mType;
-
- private String mPackageName;
-
- private int mProtectionLevel;
-
- @Nullable
private PermissionInfo mPermissionInfo;
- @Nullable
- private PermissionInfo mPendingPermissionInfo;
+ private boolean mReconciled;
+
+ private final @PermissionType int mType;
/** UID that owns the definition of this permission */
private int mUid;
@@ -109,30 +97,35 @@
private boolean mGidsPerUser;
public BasePermission(@NonNull String name, String packageName, @PermissionType int type) {
- mName = name;
- mPackageName = packageName;
- mType = type;
+ mPermissionInfo = new PermissionInfo();
+ mPermissionInfo.name = name;
+ mPermissionInfo.packageName = packageName;
// Default to most conservative protection level.
- mProtectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
+ mPermissionInfo.protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
+ mType = type;
}
@Override
public String toString() {
- return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + mName
- + "}";
+ return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " "
+ + mPermissionInfo.name + "}";
}
@NonNull
public String getName() {
- return mName;
+ return mPermissionInfo.name;
}
public int getProtectionLevel() {
- return mProtectionLevel;
+ return mPermissionInfo.protectionLevel;
}
public String getPackageName() {
- return mPackageName;
+ return mPermissionInfo.packageName;
+ }
+
+ public boolean isPermissionDefinitionChanged() {
+ return mPermissionDefinitionChanged;
}
public int getType() {
@@ -149,7 +142,20 @@
}
public void setPermissionInfo(@Nullable PermissionInfo permissionInfo) {
- mPermissionInfo = permissionInfo;
+ if (permissionInfo != null) {
+ mPermissionInfo = permissionInfo;
+ } else {
+ final PermissionInfo newPermissionInfo = new PermissionInfo();
+ newPermissionInfo.name = mPermissionInfo.name;
+ newPermissionInfo.packageName = mPermissionInfo.packageName;
+ newPermissionInfo.protectionLevel = mPermissionInfo.protectionLevel;
+ mPermissionInfo = newPermissionInfo;
+ }
+ mReconciled = permissionInfo != null;
+ }
+
+ public void setPermissionDefinitionChanged(boolean shouldOverride) {
+ mPermissionDefinitionChanged = shouldOverride;
}
public boolean hasGids() {
@@ -172,7 +178,7 @@
public int calculateFootprint(BasePermission perm) {
if (mUid == perm.mUid) {
- return perm.mName.length() + perm.mPermissionInfo.calculateFootprint();
+ return perm.mPermissionInfo.name.length() + perm.mPermissionInfo.calculateFootprint();
}
return 0;
}
@@ -190,128 +196,143 @@
}
public boolean isNormal() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_NORMAL;
}
public boolean isRuntime() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_DANGEROUS;
}
public boolean isInstalled() {
- return mPermissionInfo != null
- && (mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLED) != 0;
+ return (mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLED) != 0;
}
public boolean isRemoved() {
- return mPermissionInfo != null
- && (mPermissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0;
+ return (mPermissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0;
}
public boolean isSoftRestricted() {
- return mPermissionInfo != null
- && (mPermissionInfo.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0;
+ return (mPermissionInfo.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0;
}
public boolean isHardRestricted() {
- return mPermissionInfo != null
- && (mPermissionInfo.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0;
+ return (mPermissionInfo.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0;
}
public boolean isHardOrSoftRestricted() {
- return mPermissionInfo != null && (mPermissionInfo.flags
- & (PermissionInfo.FLAG_HARD_RESTRICTED | PermissionInfo.FLAG_SOFT_RESTRICTED)) != 0;
+ return (mPermissionInfo.flags & (PermissionInfo.FLAG_HARD_RESTRICTED
+ | PermissionInfo.FLAG_SOFT_RESTRICTED)) != 0;
}
public boolean isImmutablyRestricted() {
- return mPermissionInfo != null
- && (mPermissionInfo.flags & PermissionInfo.FLAG_IMMUTABLY_RESTRICTED) != 0;
+ return (mPermissionInfo.flags & PermissionInfo.FLAG_IMMUTABLY_RESTRICTED) != 0;
}
public boolean isInstallerExemptIgnored() {
- return mPermissionInfo != null
- && (mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLER_EXEMPT_IGNORED) != 0;
+ return (mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLER_EXEMPT_IGNORED) != 0;
}
public boolean isSignature() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_SIGNATURE;
}
public boolean isAppOp() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
}
+
public boolean isDevelopment() {
- return isSignature()
- && (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0;
+ return isSignature() && (mPermissionInfo.protectionLevel
+ & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0;
}
+
public boolean isInstaller() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0;
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0;
}
+
public boolean isInstant() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0;
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0;
}
- public boolean isOEM() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_OEM) != 0;
+
+ public boolean isOem() {
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_OEM) != 0;
}
+
public boolean isPre23() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_PRE23) != 0;
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_PRE23) != 0;
}
+
public boolean isPreInstalled() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0;
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0;
}
+
public boolean isPrivileged() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0;
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0;
}
+
public boolean isRuntimeOnly() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0;
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0;
}
+
public boolean isSetup() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0;
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0;
}
+
public boolean isVerifier() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0;
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0;
}
+
public boolean isVendorPrivileged() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0;
- }
- public boolean isSystemTextClassifier() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER)
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED)
!= 0;
}
+
+ public boolean isSystemTextClassifier() {
+ return (mPermissionInfo.protectionLevel
+ & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0;
+ }
+
public boolean isWellbeing() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0;
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0;
}
+
public boolean isDocumenter() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0;
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0;
}
+
public boolean isConfigurator() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_CONFIGURATOR)
- != 0;
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_CONFIGURATOR) != 0;
}
+
public boolean isIncidentReportApprover() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER) != 0;
+ return (mPermissionInfo.protectionLevel
+ & PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER) != 0;
}
+
public boolean isAppPredictor() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0;
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR)
+ != 0;
}
+
public boolean isCompanion() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0;
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0;
}
public boolean isRetailDemo() {
- return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0;
+ return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0;
}
public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
- if (!origPackageName.equals(mPackageName)) {
+ if (!origPackageName.equals(mPermissionInfo.packageName)) {
return;
}
- mPackageName = newPackageName;
- mPermissionInfo = null;
- if (mPendingPermissionInfo != null) {
- mPendingPermissionInfo.packageName = newPackageName;
- }
+ final PermissionInfo newPermissionInfo = new PermissionInfo();
+ newPermissionInfo.name = mPermissionInfo.name;
+ newPermissionInfo.packageName = newPackageName;
+ newPermissionInfo.protectionLevel = mPermissionInfo.protectionLevel;
+ mPermissionInfo = newPermissionInfo;
+ mReconciled = false;
mUid = 0;
mGids = EmptyArray.INT;
mGidsPerUser = false;
@@ -320,16 +341,16 @@
public boolean addToTree(@ProtectionLevel int protectionLevel,
@NonNull PermissionInfo permissionInfo, @NonNull BasePermission tree) {
final boolean changed =
- (mProtectionLevel != protectionLevel
- || mPermissionInfo == null
+ (mPermissionInfo.protectionLevel != protectionLevel
+ || !mReconciled
|| mUid != tree.mUid
|| !Objects.equals(mPermissionInfo.packageName,
tree.mPermissionInfo.packageName)
|| !comparePermissionInfos(mPermissionInfo, permissionInfo));
- mProtectionLevel = protectionLevel;
mPermissionInfo = new PermissionInfo(permissionInfo);
- mPermissionInfo.protectionLevel = protectionLevel;
mPermissionInfo.packageName = tree.mPermissionInfo.packageName;
+ mPermissionInfo.protectionLevel = protectionLevel;
+ mReconciled = true;
mUid = tree.mUid;
return changed;
}
@@ -337,13 +358,12 @@
public void updateDynamicPermission(Collection<BasePermission> permissionTrees) {
if (PackageManagerService.DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
+ getName() + " pkg=" + getPackageName()
- + " info=" + mPendingPermissionInfo);
- if (mPendingPermissionInfo != null) {
- final BasePermission tree = findPermissionTree(permissionTrees, mName);
- if (tree != null && tree.mPermissionInfo != null) {
- mPermissionInfo = new PermissionInfo(mPendingPermissionInfo);
+ + " info=" + mPermissionInfo);
+ if (mType == TYPE_DYNAMIC) {
+ final BasePermission tree = findPermissionTree(permissionTrees, mPermissionInfo.name);
+ if (tree != null) {
mPermissionInfo.packageName = tree.mPermissionInfo.packageName;
- mPermissionInfo.name = mName;
+ mReconciled = true;
mUid = tree.mUid;
}
}
@@ -354,9 +374,10 @@
@NonNull AndroidPackage pkg, Collection<BasePermission> permissionTrees,
boolean chatty) {
// Allow system apps to redefine non-system permissions
- if (bp != null && !Objects.equals(bp.mPackageName, p.packageName)) {
+ boolean ownerChanged = false;
+ if (bp != null && !Objects.equals(bp.mPermissionInfo.packageName, p.packageName)) {
final boolean currentOwnerIsSystem;
- if (bp.mPermissionInfo == null) {
+ if (!bp.mReconciled) {
currentOwnerIsSystem = false;
} else {
AndroidPackage currentPackage = packageManagerInternal.getPackage(
@@ -369,34 +390,36 @@
}
if (pkg.isSystem()) {
- if (bp.mType == BasePermission.TYPE_BUILTIN && bp.mPermissionInfo == null) {
+ if (bp.mType == BasePermission.TYPE_CONFIG && !bp.mReconciled) {
// It's a built-in permission and no owner, take ownership now
p.flags |= PermissionInfo.FLAG_INSTALLED;
bp.mPermissionInfo = p;
+ bp.mReconciled = true;
bp.mUid = pkg.getUid();
- bp.mPackageName = p.packageName;
} else if (!currentOwnerIsSystem) {
String msg = "New decl " + pkg + " of permission "
- + p.name + " is system; overriding " + bp.mPackageName;
+ + p.name + " is system; overriding " + bp.mPermissionInfo.packageName;
PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ ownerChanged = true;
bp = null;
}
}
}
if (bp == null) {
- bp = new BasePermission(p.name, p.packageName, TYPE_NORMAL);
+ bp = new BasePermission(p.name, p.packageName, TYPE_MANIFEST);
}
+ boolean wasNormal = bp.isNormal();
StringBuilder r = null;
- if (bp.mPermissionInfo == null) {
- if (bp.mPackageName == null
- || bp.mPackageName.equals(p.packageName)) {
+ if (!bp.mReconciled) {
+ if (bp.mPermissionInfo.packageName == null
+ || bp.mPermissionInfo.packageName.equals(p.packageName)) {
final BasePermission tree = findPermissionTree(permissionTrees, p.name);
if (tree == null
- || tree.mPackageName.equals(p.packageName)) {
+ || tree.mPermissionInfo.packageName.equals(p.packageName)) {
p.flags |= PermissionInfo.FLAG_INSTALLED;
bp.mPermissionInfo = p;
+ bp.mReconciled = true;
bp.mUid = pkg.getUid();
- bp.mPackageName = p.packageName;
if (chatty) {
if (r == null) {
r = new StringBuilder(256);
@@ -408,13 +431,13 @@
} else {
Slog.w(TAG, "Permission " + p.name + " from package "
+ p.packageName + " ignored: base tree "
- + tree.mName + " is from package "
- + tree.mPackageName);
+ + tree.mPermissionInfo.name + " is from package "
+ + tree.mPermissionInfo.packageName);
}
} else {
Slog.w(TAG, "Permission " + p.name + " from package "
+ p.packageName + " ignored: original from "
- + bp.mPackageName);
+ + bp.mPermissionInfo.packageName);
}
} else if (chatty) {
if (r == null) {
@@ -425,8 +448,10 @@
r.append("DUP:");
r.append(p.name);
}
- if (bp.mPermissionInfo == p) {
- bp.mProtectionLevel = p.protectionLevel;
+ if (bp.isRuntime() && (ownerChanged || wasNormal)) {
+ // If this is a runtime permission and the owner has changed, or this was a normal
+ // permission, then permission state should be cleaned up
+ bp.mPermissionDefinitionChanged = true;
}
if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
Log.d(TAG, " Permissions: " + r);
@@ -444,7 +469,7 @@
}
throw new SecurityException("Calling uid " + callingUid
+ " is not allowed to add to permission tree "
- + bp.mName + " owned by uid " + bp.mUid);
+ + bp.mPermissionInfo.name + " owned by uid " + bp.mUid);
}
}
throw new SecurityException("No permission tree found for " + permName);
@@ -453,9 +478,9 @@
private static BasePermission findPermissionTree(
Collection<BasePermission> permissionTrees, String permName) {
for (BasePermission bp : permissionTrees) {
- if (permName.startsWith(bp.mName)
- && permName.length() > bp.mName.length()
- && permName.charAt(bp.mName.length()) == '.') {
+ if (permName.startsWith(bp.mPermissionInfo.name)
+ && permName.length() > bp.mPermissionInfo.name.length()
+ && permName.charAt(bp.mPermissionInfo.name.length()) == '.') {
return bp;
}
}
@@ -464,20 +489,20 @@
@Nullable
public String getBackgroundPermission() {
- return mPermissionInfo != null ? mPermissionInfo.backgroundPermission : null;
+ return mPermissionInfo.backgroundPermission;
}
@Nullable
public String getGroup() {
- return mPermissionInfo != null ? mPermissionInfo.group : null;
+ return mPermissionInfo.group;
}
public int getProtection() {
- return mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
+ return mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
}
public int getProtectionFlags() {
- return mProtectionLevel & PermissionInfo.PROTECTION_MASK_FLAGS;
+ return mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_FLAGS;
}
@NonNull
@@ -495,17 +520,18 @@
}
} else {
permissionInfo = new PermissionInfo();
- permissionInfo.name = mName;
- permissionInfo.packageName = mPackageName;
- permissionInfo.nonLocalizedLabel = mName;
+ permissionInfo.name = mPermissionInfo.name;
+ permissionInfo.packageName = mPermissionInfo.packageName;
+ permissionInfo.nonLocalizedLabel = mPermissionInfo.name;
}
if (targetSdkVersion >= Build.VERSION_CODES.O) {
- permissionInfo.protectionLevel = mProtectionLevel;
+ permissionInfo.protectionLevel = mPermissionInfo.protectionLevel;
} else {
- final int protection = mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
+ final int protection = mPermissionInfo.protectionLevel
+ & PermissionInfo.PROTECTION_MASK_BASE;
if (protection == PermissionInfo.PROTECTION_SIGNATURE) {
// Signature permission's protection flags are always reported.
- permissionInfo.protectionLevel = mProtectionLevel;
+ permissionInfo.protectionLevel = mPermissionInfo.protectionLevel;
} else {
permissionInfo.protectionLevel = protection;
}
@@ -520,9 +546,9 @@
return false;
}
final String name = parser.getAttributeValue(null, ATTR_NAME);
- final String sourcePackage = parser.getAttributeValue(null, ATTR_PACKAGE);
+ final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
final String ptype = parser.getAttributeValue(null, "type");
- if (name == null || sourcePackage == null) {
+ if (name == null || packageName == null) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: permissions has" + " no name at "
+ parser.getPositionDescription());
@@ -531,23 +557,19 @@
final boolean dynamic = "dynamic".equals(ptype);
BasePermission bp = out.get(name);
// If the permission is builtin, do not clobber it.
- if (bp == null || bp.mType != TYPE_BUILTIN) {
- bp = new BasePermission(name.intern(), sourcePackage,
- dynamic ? TYPE_DYNAMIC : TYPE_NORMAL);
+ if (bp == null || bp.mType != TYPE_CONFIG) {
+ bp = new BasePermission(name.intern(), packageName,
+ dynamic ? TYPE_DYNAMIC : TYPE_MANIFEST);
}
- bp.mProtectionLevel = readInt(parser, null, "protection",
+ bp.mPermissionInfo.protectionLevel = readInt(parser, null, "protection",
PermissionInfo.PROTECTION_NORMAL);
- bp.mProtectionLevel = PermissionInfo.fixProtectionLevel(bp.mProtectionLevel);
+ bp.mPermissionInfo.protectionLevel = PermissionInfo.fixProtectionLevel(
+ bp.mPermissionInfo.protectionLevel);
if (dynamic) {
- final PermissionInfo pi = new PermissionInfo();
- pi.packageName = sourcePackage.intern();
- pi.name = name.intern();
- pi.icon = readInt(parser, null, "icon", 0);
- pi.nonLocalizedLabel = parser.getAttributeValue(null, "label");
- pi.protectionLevel = bp.mProtectionLevel;
- bp.mPendingPermissionInfo = pi;
+ bp.mPermissionInfo.icon = readInt(parser, null, "icon", 0);
+ bp.mPermissionInfo.nonLocalizedLabel = parser.getAttributeValue(null, "label");
}
- out.put(bp.mName, bp);
+ out.put(bp.mPermissionInfo.name, bp);
return true;
}
@@ -568,30 +590,23 @@
}
public void writeLPr(@NonNull XmlSerializer serializer) throws IOException {
- if (mPackageName == null) {
+ if (mPermissionInfo.packageName == null) {
return;
}
serializer.startTag(null, TAG_ITEM);
- serializer.attribute(null, ATTR_NAME, mName);
- serializer.attribute(null, ATTR_PACKAGE, mPackageName);
- if (mProtectionLevel != PermissionInfo.PROTECTION_NORMAL) {
- serializer.attribute(null, "protection", Integer.toString(mProtectionLevel));
+ serializer.attribute(null, ATTR_NAME, mPermissionInfo.name);
+ serializer.attribute(null, ATTR_PACKAGE, mPermissionInfo.packageName);
+ if (mPermissionInfo.protectionLevel != PermissionInfo.PROTECTION_NORMAL) {
+ serializer.attribute(null, "protection",
+ Integer.toString(mPermissionInfo.protectionLevel));
}
- if (mType == BasePermission.TYPE_DYNAMIC) {
- if (mPermissionInfo != null || mPendingPermissionInfo != null) {
- serializer.attribute(null, "type", "dynamic");
- int icon = mPermissionInfo != null ? mPermissionInfo.icon
- : mPendingPermissionInfo.icon;
- CharSequence nonLocalizedLabel = mPermissionInfo != null
- ? mPermissionInfo.nonLocalizedLabel
- : mPendingPermissionInfo.nonLocalizedLabel;
-
- if (icon != 0) {
- serializer.attribute(null, "icon", Integer.toString(icon));
- }
- if (nonLocalizedLabel != null) {
- serializer.attribute(null, "label", nonLocalizedLabel.toString());
- }
+ if (mType == TYPE_DYNAMIC) {
+ serializer.attribute(null, "type", "dynamic");
+ if (mPermissionInfo.icon != 0) {
+ serializer.attribute(null, "icon", Integer.toString(mPermissionInfo.icon));
+ }
+ if (mPermissionInfo.nonLocalizedLabel != null) {
+ serializer.attribute(null, "label", mPermissionInfo.nonLocalizedLabel.toString());
}
}
serializer.endTag(null, TAG_ITEM);
@@ -616,10 +631,10 @@
public boolean dumpPermissionsLPr(@NonNull PrintWriter pw, @NonNull String packageName,
@NonNull Set<String> permissionNames, boolean readEnforced,
boolean printedSomething, @NonNull DumpState dumpState) {
- if (packageName != null && !packageName.equals(mPackageName)) {
+ if (packageName != null && !packageName.equals(mPermissionInfo.packageName)) {
return false;
}
- if (permissionNames != null && !permissionNames.contains(mName)) {
+ if (permissionNames != null && !permissionNames.contains(mPermissionInfo.name)) {
return false;
}
if (!printedSomething) {
@@ -627,15 +642,15 @@
pw.println();
pw.println("Permissions:");
}
- pw.print(" Permission ["); pw.print(mName); pw.print("] (");
+ pw.print(" Permission ["); pw.print(mPermissionInfo.name); pw.print("] (");
pw.print(Integer.toHexString(System.identityHashCode(this)));
pw.println("):");
- pw.print(" sourcePackage="); pw.println(mPackageName);
+ pw.print(" sourcePackage="); pw.println(mPermissionInfo.packageName);
pw.print(" uid="); pw.print(mUid);
pw.print(" gids="); pw.print(Arrays.toString(computeGids(UserHandle.USER_SYSTEM)));
pw.print(" type="); pw.print(mType);
pw.print(" prot=");
- pw.println(PermissionInfo.protectionToString(mProtectionLevel));
+ pw.println(PermissionInfo.protectionToString(mPermissionInfo.protectionLevel));
if (mPermissionInfo != null) {
pw.print(" perm="); pw.println(mPermissionInfo);
if ((mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0
@@ -643,7 +658,8 @@
pw.print(" flags=0x"); pw.println(Integer.toHexString(mPermissionInfo.flags));
}
}
- if (READ_EXTERNAL_STORAGE.equals(mName)) {
+ if (Objects.equals(mPermissionInfo.name,
+ android.Manifest.permission.READ_EXTERNAL_STORAGE)) {
pw.print(" enforced=");
pw.println(readEnforced);
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index d871325..da4ef63 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -411,7 +411,7 @@
final SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
BasePermission bp = mSettings.getPermissionLocked(perm.name);
if (bp == null) {
- bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
+ bp = new BasePermission(perm.name, "android", BasePermission.TYPE_CONFIG);
mSettings.putPermissionLocked(perm.name, bp);
}
if (perm.gids != null) {
@@ -2327,8 +2327,74 @@
}
}
- private void addAllPermissions(AndroidPackage pkg, boolean chatty) {
+ /**
+ * If permissions are upgraded to runtime, or their owner changes to the system, then any
+ * granted permissions must be revoked.
+ *
+ * @param permissionsToRevoke A list of permission names to revoke
+ * @param allPackageNames All package names
+ * @param permissionCallback Callback for permission changed
+ */
+ private void revokeRuntimePermissionsIfPermissionDefinitionChanged(
+ @NonNull List<String> permissionsToRevoke,
+ @NonNull ArrayList<String> allPackageNames,
+ @NonNull PermissionCallback permissionCallback) {
+
+ final int[] userIds = mUserManagerInt.getUserIds();
+ final int numPermissions = permissionsToRevoke.size();
+ final int numUserIds = userIds.length;
+ final int numPackages = allPackageNames.size();
+ final int callingUid = Binder.getCallingUid();
+
+ for (int permNum = 0; permNum < numPermissions; permNum++) {
+ String permName = permissionsToRevoke.get(permNum);
+ BasePermission bp = mSettings.getPermission(permName);
+ if (bp == null || !bp.isRuntime()) {
+ continue;
+ }
+ for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
+ final int userId = userIds[userIdNum];
+ for (int packageNum = 0; packageNum < numPackages; packageNum++) {
+ final String packageName = allPackageNames.get(packageNum);
+ final int uid = mPackageManagerInt.getPackageUid(packageName, 0, userId);
+ if (uid < Process.FIRST_APPLICATION_UID) {
+ // do not revoke from system apps
+ continue;
+ }
+ final int permissionState = checkPermissionImpl(permName, packageName,
+ userId);
+ final int flags = getPermissionFlags(permName, packageName, userId);
+ final int flagMask = FLAG_PERMISSION_SYSTEM_FIXED
+ | FLAG_PERMISSION_POLICY_FIXED
+ | FLAG_PERMISSION_GRANTED_BY_DEFAULT
+ | FLAG_PERMISSION_GRANTED_BY_ROLE;
+ if (permissionState == PackageManager.PERMISSION_GRANTED
+ && (flags & flagMask) == 0) {
+ EventLog.writeEvent(0x534e4554, "154505240", uid,
+ "Revoking permission " + permName + " from package "
+ + packageName + " due to definition change");
+ EventLog.writeEvent(0x534e4554, "168319670", uid,
+ "Revoking permission " + permName + " from package "
+ + packageName + " due to definition change");
+ Slog.e(TAG, "Revoking permission " + permName + " from package "
+ + packageName + " due to definition change");
+ try {
+ revokeRuntimePermissionInternal(permName, packageName,
+ false, callingUid, userId, null, permissionCallback);
+ } catch (Exception e) {
+ Slog.e(TAG, "Could not revoke " + permName + " from "
+ + packageName, e);
+ }
+ }
+ }
+ }
+ bp.setPermissionDefinitionChanged(false);
+ }
+ }
+
+ private List<String> addAllPermissions(AndroidPackage pkg, boolean chatty) {
final int N = ArrayUtils.size(pkg.getPermissions());
+ ArrayList<String> definitionChangedPermissions = new ArrayList<>();
for (int i=0; i<N; i++) {
ParsedPermission p = pkg.getPermissions().get(i);
@@ -2369,8 +2435,12 @@
if (bp.isInstalled()) {
p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED);
}
+ if (bp.isPermissionDefinitionChanged()) {
+ definitionChangedPermissions.add(p.getName());
+ }
}
}
+ return definitionChangedPermissions;
}
private void addAllPermissionGroups(AndroidPackage pkg, boolean chatty) {
@@ -2988,7 +3058,7 @@
for (String permission : ps.getGrantedPermissions()) {
if (!pkg.getImplicitPermissions().contains(permission)) {
BasePermission bp = mSettings.getPermissionLocked(permission);
- if (bp.isRuntime()) {
+ if (bp != null && bp.isRuntime()) {
int flags = ps.getPermissionFlags(permission);
if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
@@ -3300,7 +3370,7 @@
PackageParser.SigningDetails.CertCapabilities.PERMISSION);
final boolean isVendorPrivilegedPermission = bp.isVendorPrivileged();
final boolean isPrivilegedPermission = bp.isPrivileged() || isVendorPrivilegedPermission;
- final boolean isOemPermission = bp.isOEM();
+ final boolean isOemPermission = bp.isOem();
if (!allowed && (isPrivilegedPermission || isOemPermission) && pkg.isSystem()) {
final String permissionName = bp.getName();
// For updated system applications, a privileged/oem permission
@@ -4750,9 +4820,18 @@
PermissionManagerService.this.revokeRuntimePermissionsIfGroupChanged(newPackage,
oldPackage, allPackageNames, mDefaultPermissionCallback);
}
+
@Override
- public void addAllPermissions(AndroidPackage pkg, boolean chatty) {
- PermissionManagerService.this.addAllPermissions(pkg, chatty);
+ public void revokeRuntimePermissionsIfPermissionDefinitionChanged(
+ @NonNull List<String> permissionsToRevoke,
+ @NonNull ArrayList<String> allPackageNames) {
+ PermissionManagerService.this.revokeRuntimePermissionsIfPermissionDefinitionChanged(
+ permissionsToRevoke, allPackageNames, mDefaultPermissionCallback);
+ }
+
+ @Override
+ public List<String> addAllPermissions(AndroidPackage pkg, boolean chatty) {
+ return PermissionManagerService.this.addAllPermissions(pkg, chatty);
}
@Override
public void addAllPermissionGroups(AndroidPackage pkg, boolean chatty) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 5ea3458..20e9c5d 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -256,12 +256,26 @@
@NonNull ArrayList<String> allPackageNames);
/**
+ * Some permissions might have been owned by a non-system package, and the system then defined
+ * said permission. Some other permissions may one have been install permissions, but are now
+ * runtime or higher. These permissions should be revoked.
+ *
+ * @param permissionsToRevoke A list of permission names to revoke
+ * @param allPackageNames All packages
+ */
+ public abstract void revokeRuntimePermissionsIfPermissionDefinitionChanged(
+ @NonNull List<String> permissionsToRevoke,
+ @NonNull ArrayList<String> allPackageNames);
+
+ /**
* Add all permissions in the given package.
* <p>
* NOTE: argument {@code groupTEMP} is temporary until mPermissionGroups is moved to
* the permission settings.
+ *
+ * @return A list of BasePermissions that were updated, and need to be revoked from packages
*/
- public abstract void addAllPermissions(@NonNull AndroidPackage pkg, boolean chatty);
+ public abstract List<String> addAllPermissions(@NonNull AndroidPackage pkg, boolean chatty);
public abstract void addAllPermissionGroups(@NonNull AndroidPackage pkg, boolean chatty);
public abstract void removeAllPermissions(@NonNull AndroidPackage pkg, boolean chatty);
diff --git a/services/core/java/com/android/server/powerstats/BatteryTrigger.java b/services/core/java/com/android/server/powerstats/BatteryTrigger.java
index c1f97f2..6500523 100644
--- a/services/core/java/com/android/server/powerstats/BatteryTrigger.java
+++ b/services/core/java/com/android/server/powerstats/BatteryTrigger.java
@@ -21,7 +21,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
-import android.util.Log;
+import android.util.Slog;
/**
* BatteryTrigger instantiates a BroadcastReceiver that listens for changes
@@ -42,7 +42,7 @@
int newBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
if (newBatteryLevel < mBatteryLevel) {
- if (DEBUG) Log.d(TAG, "Battery level dropped. Log rail data");
+ if (DEBUG) Slog.d(TAG, "Battery level dropped. Log rail data");
logPowerStatsData();
}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsData.java b/services/core/java/com/android/server/powerstats/PowerStatsData.java
deleted file mode 100644
index 755bd5f..0000000
--- a/services/core/java/com/android/server/powerstats/PowerStatsData.java
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * 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.powerstats;
-
-import android.util.Log;
-import android.util.proto.ProtoInputStream;
-import android.util.proto.ProtoOutputStream;
-import android.util.proto.ProtoUtils;
-import android.util.proto.WireTypeMismatchException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * PowerStatsData is a class that performs two operations:
- * 1) Unpacks serialized protobuf byte arrays, as defined in powerstatsservice.proto,
- * into RailInfo or EnergyData object arrays.
- *
- * 2) Packs RailInfo or EnergyData object arrays in protobuf byte arrays as
- * defined in powerstatsservice.proto.
- *
- * Inside frameworks, proto source is generated with the genstream option
- * and therefore the getter/setter helper functions are not available.
- * The protos need to be packed/unpacked in a more manual way using
- * ProtoOutputStream/ProtoInputStream.
- */
-public class PowerStatsData {
- private static final String TAG = PowerStatsData.class.getSimpleName();
-
- private List<Data> mDataList;
-
- public PowerStatsData(ProtoInputStream pis) throws IOException {
- mDataList = new ArrayList<Data>();
- unpackProto(pis);
- }
-
- public PowerStatsData(Data[] data) {
- mDataList = new ArrayList<Data>(Arrays.asList(data));
- }
-
- private void unpackProto(ProtoInputStream pis) throws IOException {
- long token;
-
- while (true) {
- try {
- switch (pis.nextField()) {
- case (int) PowerStatsServiceProto.RAIL_INFO:
- token = pis.start(PowerStatsServiceProto.RAIL_INFO);
- mDataList.add(new RailInfo(pis));
- pis.end(token);
- break;
-
- case (int) PowerStatsServiceProto.ENERGY_DATA:
- token = pis.start(PowerStatsServiceProto.ENERGY_DATA);
- mDataList.add(new EnergyData(pis));
- pis.end(token);
- break;
-
- case ProtoInputStream.NO_MORE_FIELDS:
- return;
-
- default:
- Log.e(TAG, "Unhandled field in proto: "
- + ProtoUtils.currentFieldToString(pis));
- break;
- }
- } catch (WireTypeMismatchException wtme) {
- Log.e(TAG, "Wire Type mismatch in proto: " + ProtoUtils.currentFieldToString(pis));
- }
- }
- }
-
- /**
- * Write this object to an output stream in protobuf format.
- *
- * @param pos ProtoOutputStream of file where data is to be written. Data is
- * written in protobuf format as defined by powerstatsservice.proto.
- */
- public void toProto(ProtoOutputStream pos) {
- long token;
-
- for (Data data : mDataList) {
- if (data instanceof RailInfo) {
- token = pos.start(PowerStatsServiceProto.RAIL_INFO);
- } else {
- token = pos.start(PowerStatsServiceProto.ENERGY_DATA);
- }
- data.toProto(pos);
- pos.end(token);
- }
- }
-
- /**
- * Convert mDataList to proto format and return the serialized byte array.
- *
- * @return byte array containing a serialized protobuf of mDataList.
- */
- public byte[] getProtoBytes() {
- ProtoOutputStream pos = new ProtoOutputStream();
- long token;
-
- for (Data data : mDataList) {
- if (data instanceof RailInfo) {
- token = pos.start(PowerStatsServiceProto.RAIL_INFO);
- } else {
- token = pos.start(PowerStatsServiceProto.ENERGY_DATA);
- }
- data.toProto(pos);
- pos.end(token);
- }
- return pos.getBytes();
- }
-
- /**
- * Print this object to logcat.
- */
- public void print() {
- for (Data data : mDataList) {
- Log.d(TAG, data.toString());
- }
- }
-
- /**
- * RailInfo is a class that stores a description for an individual ODPM
- * rail. It provides functionality to unpack a RailInfo object from a
- * serialized protobuf byte array, and to pack a RailInfo object into
- * a ProtoOutputStream.
- */
- public static class RailInfo extends Data {
- public String mRailName;
- public String mSubSysName;
- public long mSamplingRate;
-
- public RailInfo(ProtoInputStream pis) throws IOException {
- unpackProto(pis);
- }
-
- public RailInfo(long index, String railName, String subSysName, long samplingRate) {
- mIndex = index;
- mRailName = railName;
- mSubSysName = subSysName;
- mSamplingRate = samplingRate;
- }
-
- @Override
- protected void unpackProto(ProtoInputStream pis) throws IOException {
- while (true) {
- try {
- switch (pis.nextField()) {
- case (int) RailInfoProto.INDEX:
- mIndex = pis.readInt(RailInfoProto.INDEX);
- break;
-
- case (int) RailInfoProto.RAIL_NAME:
- mRailName = pis.readString(RailInfoProto.RAIL_NAME);
- break;
-
- case (int) RailInfoProto.SUBSYS_NAME:
- mSubSysName = pis.readString(RailInfoProto.SUBSYS_NAME);
- break;
-
- case (int) RailInfoProto.SAMPLING_RATE:
- mSamplingRate = pis.readInt(RailInfoProto.SAMPLING_RATE);
- break;
-
- case ProtoInputStream.NO_MORE_FIELDS:
- return;
-
- default:
- Log.e(TAG, "Unhandled field in RailInfoProto: "
- + ProtoUtils.currentFieldToString(pis));
- break;
- }
- } catch (WireTypeMismatchException wtme) {
- Log.e(TAG, "Wire Type mismatch in RailInfoProto: "
- + ProtoUtils.currentFieldToString(pis));
- }
- }
- }
-
- @Override
- public void toProto(ProtoOutputStream pos) {
- pos.write(RailInfoProto.INDEX, mIndex);
- pos.write(RailInfoProto.RAIL_NAME, mRailName);
- pos.write(RailInfoProto.SUBSYS_NAME, mSubSysName);
- pos.write(RailInfoProto.SAMPLING_RATE, mSamplingRate);
- }
-
- @Override
- public String toString() {
- return String.format("Index = " + mIndex
- + ", RailName = " + mRailName
- + ", SubSysName = " + mSubSysName
- + ", SamplingRate = " + mSamplingRate);
- }
- }
-
- /**
- * EnergyData is a class that stores an energy (uWs) data reading for an
- * individual ODPM rail. It provides functionality to unpack an EnergyData
- * object from a serialized protobuf byte array, and to pack an EnergyData
- * object into a ProtoOutputStream.
- */
- public static class EnergyData extends Data {
- public long mTimestampMs;
- public long mEnergyUWs;
-
- public EnergyData(ProtoInputStream pis) throws IOException {
- unpackProto(pis);
- }
-
- public EnergyData(long index, long timestampMs, long energyUWs) {
- mIndex = index;
- mTimestampMs = timestampMs;
- mEnergyUWs = energyUWs;
- }
-
- @Override
- protected void unpackProto(ProtoInputStream pis) throws IOException {
- while (true) {
- try {
- switch (pis.nextField()) {
- case (int) EnergyDataProto.INDEX:
- mIndex = pis.readInt(EnergyDataProto.INDEX);
- break;
-
- case (int) EnergyDataProto.TIMESTAMP_MS:
- mTimestampMs = pis.readLong(EnergyDataProto.TIMESTAMP_MS);
- break;
-
- case (int) EnergyDataProto.ENERGY_UWS:
- mEnergyUWs = pis.readLong(EnergyDataProto.ENERGY_UWS);
- break;
-
- case ProtoInputStream.NO_MORE_FIELDS:
- return;
-
- default:
- Log.e(TAG, "Unhandled field in EnergyDataProto: "
- + ProtoUtils.currentFieldToString(pis));
- break;
- }
- } catch (WireTypeMismatchException wtme) {
- Log.e(TAG, "Wire Type mismatch in EnergyDataProto: "
- + ProtoUtils.currentFieldToString(pis));
- }
- }
- }
-
- @Override
- protected void toProto(ProtoOutputStream pos) {
- pos.write(EnergyDataProto.INDEX, mIndex);
- pos.write(EnergyDataProto.TIMESTAMP_MS, mTimestampMs);
- pos.write(EnergyDataProto.ENERGY_UWS, mEnergyUWs);
- }
-
- @Override
- public String toString() {
- return String.format("Index = " + mIndex
- + ", Timestamp (ms) = " + mTimestampMs
- + ", Energy (uWs) = " + mEnergyUWs);
- }
- }
-
- private abstract static class Data {
- public long mIndex;
- protected abstract void unpackProto(ProtoInputStream pis) throws IOException;
- protected abstract void toProto(ProtoOutputStream pos);
- }
-}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
index 84a6fc9..9829357 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
@@ -17,7 +17,7 @@
package com.android.server.powerstats;
import android.content.Context;
-import android.util.Log;
+import android.util.Slog;
import com.android.internal.util.FileRotator;
@@ -127,7 +127,7 @@
DataElement dataElement = new DataElement(in);
mCallback.onReadDataElement(dataElement.getData());
} catch (IOException e) {
- Log.e(TAG, "Failed to read from storage. " + e.getMessage());
+ Slog.e(TAG, "Failed to read from storage. " + e.getMessage());
}
}
}
@@ -170,7 +170,7 @@
mDataStorageDir = dataStoragePath;
if (!mDataStorageDir.exists() && !mDataStorageDir.mkdirs()) {
- Log.wtf(TAG, "mDataStorageDir does not exist: " + mDataStorageDir.getPath());
+ Slog.wtf(TAG, "mDataStorageDir does not exist: " + mDataStorageDir.getPath());
mFileRotator = null;
} else {
// Delete files written with an old version number. The version is included in the
@@ -198,19 +198,21 @@
* array and written to on-device storage.
*/
public void write(byte[] data) {
- mLock.lock();
+ if (data.length > 0) {
+ mLock.lock();
- long currentTimeMillis = System.currentTimeMillis();
- try {
- DataElement dataElement = new DataElement(data);
- mFileRotator.rewriteActive(new DataRewriter(dataElement.toByteArray()),
- currentTimeMillis);
- mFileRotator.maybeRotate(currentTimeMillis);
- } catch (IOException e) {
- Log.e(TAG, "Failed to write to on-device storage: " + e);
+ long currentTimeMillis = System.currentTimeMillis();
+ try {
+ DataElement dataElement = new DataElement(data);
+ mFileRotator.rewriteActive(new DataRewriter(dataElement.toByteArray()),
+ currentTimeMillis);
+ mFileRotator.maybeRotate(currentTimeMillis);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write to on-device storage: " + e);
+ }
+
+ mLock.unlock();
}
-
- mLock.unlock();
}
/**
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
index dc996a3..18646b9 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
@@ -16,6 +16,17 @@
package com.android.server.powerstats;
+import android.hardware.power.stats.IPowerStats;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.function.Supplier;
+
/**
* PowerStatsHALWrapper is a wrapper class for the PowerStats HAL API calls.
*/
@@ -27,30 +38,50 @@
*/
public interface IPowerStatsHALWrapper {
/**
- * Returns rail info for all available ODPM rails.
+ * Returns the energy consumer IDs for all available energy consumers (power models) on the
+ * device. Examples of subsystems for which energy consumer results (power models)
+ * may be available are GPS, display, wifi, etc. The default list of energy
+ * consumers can be found in the PowerStats HAL definition (EnergyConsumerId.aidl).
+ * The availability of energy consumer IDs is hardware dependent.
*
- * @return array of RailInfo objects containing rail info for all
- * available rails.
+ * @return List of EnergyConsumerIds all available energy consumers.
*/
- PowerStatsData.RailInfo[] readRailInfo();
+ int[] getEnergyConsumerInfo();
/**
- * Returns energy data for all available ODPM rails. Available rails can
- * be retrieved by calling nativeGetRailInfo. Energy data and
- * rail info can be linked through the index field.
+ * Returns the energy consumer result for all available energy consumers (power models).
+ * Available consumers can be retrieved by calling getEnergyConsumerInfo(). The
+ * subsystem corresponding to the energy consumer result is defined by the energy
+ * consumer ID.
*
- * @return array of EnergyData objects containing energy data for all
- * available rails.
+ * @return List of EnergyConsumerResult objects containing energy consumer results for all
+ * available energy consumers (power models).
*/
- PowerStatsData.EnergyData[] readEnergyData();
+ android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed();
+
+ /**
+ * Returns channel info for all available energy meters.
+ *
+ * @return List of ChannelInfo objects containing channel info for all available energy
+ * meters.
+ */
+ android.hardware.power.stats.ChannelInfo[] getEnergyMeterInfo();
+
+ /**
+ * Returns energy measurements for all available energy meters. Available channels can be
+ * retrieved by calling getEnergyMeterInfo(). Energy measurements and channel info
+ * can be linked through the channelId field.
+ *
+ * @return List of EnergyMeasurement objects containing energy measurements for all
+ * available energy meters.
+ */
+ android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters();
/**
* Returns boolean indicating if connection to power stats HAL was
* established.
*
- * @return true if connection to power stats HAL was correctly established
- * and that energy data and rail info can be read from the interface.
- * false otherwise
+ * @return true if connection to power stats HAL was correctly established.
*/
boolean initialize();
}
@@ -61,45 +92,108 @@
* framework and will be passed into the PowerStatsService through an injector.
*/
public static final class PowerStatsHALWrapperImpl implements IPowerStatsHALWrapper {
- private static native boolean nativeInit();
- private static native PowerStatsData.RailInfo[] nativeGetRailInfo();
- private static native PowerStatsData.EnergyData[] nativeGetEnergyData();
+ private static Supplier<IPowerStats> sVintfPowerStats;
- /**
- * Returns rail info for all available ODPM rails.
- *
- * @return array of RailInfo objects containing rail info for all
- * available rails.
- */
@Override
- public PowerStatsData.RailInfo[] readRailInfo() {
- return nativeGetRailInfo();
+ public int[] getEnergyConsumerInfo() {
+ int[] energyConsumerInfoHAL = null;
+
+ if (sVintfPowerStats != null) {
+ try {
+ energyConsumerInfoHAL = sVintfPowerStats.get().getEnergyConsumerInfo();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get energy consumer info from PowerStats HAL");
+ }
+ }
+
+ return energyConsumerInfoHAL;
}
- /**
- * Returns energy data for all available ODPM rails. Available rails can
- * be retrieved by calling nativeGetRailInfo. Energy data and
- * rail info can be linked through the index field.
- *
- * @return array of EnergyData objects containing energy data for all
- * available rails.
- */
@Override
- public PowerStatsData.EnergyData[] readEnergyData() {
- return nativeGetEnergyData();
+ public android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed() {
+ android.hardware.power.stats.EnergyConsumerResult[] energyConsumedHAL = null;
+
+ if (sVintfPowerStats != null) {
+ try {
+ energyConsumedHAL =
+ sVintfPowerStats.get().getEnergyConsumed(new int[0]);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get energy consumer results from PowerStats HAL");
+ }
+ }
+
+ return energyConsumedHAL;
}
- /**
- * Returns boolean indicating if connection to power stats HAL was
- * established.
- *
- * @return true if connection to power stats HAL was correctly established
- * and that energy data and rail info can be read from the interface.
- * false otherwise
- */
+ @Override
+ public android.hardware.power.stats.ChannelInfo[] getEnergyMeterInfo() {
+ android.hardware.power.stats.ChannelInfo[] energyMeterInfoHAL = null;
+
+ if (sVintfPowerStats != null) {
+ try {
+ energyMeterInfoHAL = sVintfPowerStats.get().getEnergyMeterInfo();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get energy meter info from PowerStats HAL");
+ }
+ }
+
+ return energyMeterInfoHAL;
+ }
+
+ @Override
+ public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters() {
+ android.hardware.power.stats.EnergyMeasurement[] energyMeasurementHAL = null;
+
+ if (sVintfPowerStats != null) {
+ try {
+ energyMeasurementHAL =
+ sVintfPowerStats.get().readEnergyMeters(new int[0]);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get energy measurements from PowerStats HAL");
+ }
+ }
+
+ return energyMeasurementHAL;
+ }
+
@Override
public boolean initialize() {
- return nativeInit();
+ Supplier<IPowerStats> service = new VintfHalCache();
+
+ if (service.get() == null) {
+ sVintfPowerStats = null;
+ return false;
+ } else {
+ sVintfPowerStats = service;
+ return true;
+ }
+ }
+ }
+
+ private static class VintfHalCache implements Supplier<IPowerStats>, IBinder.DeathRecipient {
+ @GuardedBy("this")
+ private IPowerStats mInstance = null;
+
+ @Override
+ public synchronized IPowerStats get() {
+ if (mInstance == null) {
+ IBinder binder = Binder.allowBlocking(ServiceManager.waitForDeclaredService(
+ "android.hardware.power.stats.IPowerStats/default"));
+ if (binder != null) {
+ mInstance = IPowerStats.Stub.asInterface(binder);
+ try {
+ binder.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to register DeathRecipient for " + mInstance);
+ }
+ }
+ }
+ return mInstance;
+ }
+
+ @Override
+ public synchronized void binderDied() {
+ mInstance = null;
}
}
}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
index 71a34a4..f5131c4 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -17,14 +17,21 @@
package com.android.server.powerstats;
import android.content.Context;
+import android.hardware.power.stats.ChannelInfo;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyMeasurement;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.util.Log;
+import android.util.Slog;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
+import com.android.server.powerstats.ProtoStreamUtils.ChannelInfoUtils;
+import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerIdUtils;
+import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerResultUtils;
+import com.android.server.powerstats.ProtoStreamUtils.EnergyMeasurementUtils;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -32,50 +39,60 @@
import java.io.IOException;
/**
- * PowerStatsLogger is responsible for logging energy data to on-device
- * storage. Messages are sent to its message handler to request that energy
- * data be logged, at which time it queries the PowerStats HAL and logs the
- * data to on-device storage. The on-device storage is dumped to file by
- * calling writeToFile with a file descriptor that points to the output file.
+ * PowerStatsLogger is responsible for logging model and meter energy data to on-device storage.
+ * Messages are sent to its message handler to request that energy data be logged, at which time it
+ * queries the PowerStats HAL and logs the data to on-device storage. The on-device storage is
+ * dumped to file by calling writeModelDataToFile or writeMeterDataToFile with a file descriptor
+ * that points to the output file.
*/
public final class PowerStatsLogger extends Handler {
private static final String TAG = PowerStatsLogger.class.getSimpleName();
private static final boolean DEBUG = false;
protected static final int MSG_LOG_TO_DATA_STORAGE = 0;
- private final PowerStatsDataStorage mPowerStatsDataStorage;
+ private final PowerStatsDataStorage mPowerStatsMeterStorage;
+ private final PowerStatsDataStorage mPowerStatsModelStorage;
private final IPowerStatsHALWrapper mPowerStatsHALWrapper;
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_LOG_TO_DATA_STORAGE:
- if (DEBUG) Log.d(TAG, "Logging to data storage");
- PowerStatsData energyData =
- new PowerStatsData(mPowerStatsHALWrapper.readEnergyData());
- mPowerStatsDataStorage.write(energyData.getProtoBytes());
+ if (DEBUG) Slog.d(TAG, "Logging to data storage");
+
+ // Log power meter data.
+ EnergyMeasurement[] energyMeasurements = mPowerStatsHALWrapper.readEnergyMeters();
+ mPowerStatsMeterStorage.write(
+ EnergyMeasurementUtils.getProtoBytes(energyMeasurements));
+ if (DEBUG) EnergyMeasurementUtils.print(energyMeasurements);
+
+ // Log power model data.
+ EnergyConsumerResult[] energyConsumerResults =
+ mPowerStatsHALWrapper.getEnergyConsumed();
+ mPowerStatsModelStorage.write(
+ EnergyConsumerResultUtils.getProtoBytes(energyConsumerResults));
+ if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResults);
break;
}
}
/**
- * Writes data stored in PowerStatsDataStorage to a file descriptor.
+ * Writes meter data stored in PowerStatsDataStorage to a file descriptor.
*
- * @param fd FileDescriptor where data stored in PowerStatsDataStorage is
- * written. Data is written in protobuf format as defined by
- * powerstatsservice.proto.
+ * @param fd FileDescriptor where meter data stored in PowerStatsDataStorage is written. Data
+ * is written in protobuf format as defined by powerstatsservice.proto.
*/
- public void writeToFile(FileDescriptor fd) {
- if (DEBUG) Log.d(TAG, "Writing to file");
+ public void writeMeterDataToFile(FileDescriptor fd) {
+ if (DEBUG) Slog.d(TAG, "Writing meter data to file");
final ProtoOutputStream pos = new ProtoOutputStream(fd);
try {
- PowerStatsData railInfo = new PowerStatsData(mPowerStatsHALWrapper.readRailInfo());
- railInfo.toProto(pos);
- if (DEBUG) railInfo.print();
+ ChannelInfo[] channelInfo = mPowerStatsHALWrapper.getEnergyMeterInfo();
+ ChannelInfoUtils.packProtoMessage(channelInfo, pos);
+ if (DEBUG) ChannelInfoUtils.print(channelInfo);
- mPowerStatsDataStorage.read(new PowerStatsDataStorage.DataElementReadCallback() {
+ mPowerStatsMeterStorage.read(new PowerStatsDataStorage.DataElementReadCallback() {
@Override
public void onReadDataElement(byte[] data) {
try {
@@ -84,26 +101,70 @@
// TODO(b/166535853): ProtoOutputStream doesn't provide a method to write
// a byte array that already contains a serialized proto, so I have to
// deserialize, then re-serialize. This is computationally inefficient.
- final PowerStatsData energyData = new PowerStatsData(pis);
- energyData.toProto(pos);
- if (DEBUG) energyData.print();
+ EnergyMeasurement[] energyMeasurement =
+ EnergyMeasurementUtils.unpackProtoMessage(data);
+ EnergyMeasurementUtils.packProtoMessage(energyMeasurement, pos);
+ if (DEBUG) EnergyMeasurementUtils.print(energyMeasurement);
} catch (IOException e) {
- Log.e(TAG, "Failed to write energy data to incident report.");
+ Slog.e(TAG, "Failed to write energy meter data to incident report.");
}
}
});
} catch (IOException e) {
- Log.e(TAG, "Failed to write rail info to incident report.");
+ Slog.e(TAG, "Failed to write energy meter info to incident report.");
}
pos.flush();
}
- public PowerStatsLogger(Context context, File dataStoragePath, String dataStorageFilename,
- IPowerStatsHALWrapper powerStatsHALWrapper) {
+ /**
+ * Writes model data stored in PowerStatsDataStorage to a file descriptor.
+ *
+ * @param fd FileDescriptor where model data stored in PowerStatsDataStorage is written. Data
+ * is written in protobuf format as defined by powerstatsservice.proto.
+ */
+ public void writeModelDataToFile(FileDescriptor fd) {
+ if (DEBUG) Slog.d(TAG, "Writing model data to file");
+
+ final ProtoOutputStream pos = new ProtoOutputStream(fd);
+
+ try {
+ int[] energyConsumerId = mPowerStatsHALWrapper.getEnergyConsumerInfo();
+ EnergyConsumerIdUtils.packProtoMessage(energyConsumerId, pos);
+ if (DEBUG) EnergyConsumerIdUtils.print(energyConsumerId);
+
+ mPowerStatsModelStorage.read(new PowerStatsDataStorage.DataElementReadCallback() {
+ @Override
+ public void onReadDataElement(byte[] data) {
+ try {
+ final ProtoInputStream pis =
+ new ProtoInputStream(new ByteArrayInputStream(data));
+ // TODO(b/166535853): ProtoOutputStream doesn't provide a method to write
+ // a byte array that already contains a serialized proto, so I have to
+ // deserialize, then re-serialize. This is computationally inefficient.
+ EnergyConsumerResult[] energyConsumerResult =
+ EnergyConsumerResultUtils.unpackProtoMessage(data);
+ EnergyConsumerResultUtils.packProtoMessage(energyConsumerResult, pos);
+ if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResult);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write energy model data to incident report.");
+ }
+ }
+ });
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write energy model info to incident report.");
+ }
+
+ pos.flush();
+ }
+
+ public PowerStatsLogger(Context context, File dataStoragePath, String meterFilename,
+ String modelFilename, IPowerStatsHALWrapper powerStatsHALWrapper) {
super(Looper.getMainLooper());
mPowerStatsHALWrapper = powerStatsHALWrapper;
- mPowerStatsDataStorage = new PowerStatsDataStorage(context, dataStoragePath,
- dataStorageFilename);
+ mPowerStatsMeterStorage = new PowerStatsDataStorage(context, dataStoragePath,
+ meterFilename);
+ mPowerStatsModelStorage = new PowerStatsDataStorage(context, dataStoragePath,
+ modelFilename);
}
}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 81883f3..bf3919e 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -18,16 +18,19 @@
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.power.stats.ChannelInfo;
import android.os.Binder;
import android.os.Environment;
import android.os.UserHandle;
-import android.util.Log;
+import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
import com.android.server.powerstats.PowerStatsHALWrapper.PowerStatsHALWrapperImpl;
+import com.android.server.powerstats.ProtoStreamUtils.ChannelInfoUtils;
+import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerIdUtils;
import java.io.File;
import java.io.FileDescriptor;
@@ -43,7 +46,8 @@
private static final boolean DEBUG = false;
private static final String DATA_STORAGE_SUBDIR = "powerstats";
private static final int DATA_STORAGE_VERSION = 0;
- private static final String DATA_STORAGE_FILENAME = "log.powerstats." + DATA_STORAGE_VERSION;
+ private static final String METER_FILENAME = "log.powerstats.meter." + DATA_STORAGE_VERSION;
+ private static final String MODEL_FILENAME = "log.powerstats.model." + DATA_STORAGE_VERSION;
private final Injector mInjector;
@@ -63,8 +67,12 @@
DATA_STORAGE_SUBDIR);
}
- String createDataStorageFilename() {
- return DATA_STORAGE_FILENAME;
+ String createMeterFilename() {
+ return METER_FILENAME;
+ }
+
+ String createModelFilename() {
+ return MODEL_FILENAME;
}
IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() {
@@ -72,9 +80,10 @@
}
PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
- String dataStorageFilename, IPowerStatsHALWrapper powerStatsHALWrapper) {
- return new PowerStatsLogger(context, dataStoragePath, dataStorageFilename,
- powerStatsHALWrapper);
+ String meterFilename, String modelFilename,
+ IPowerStatsHALWrapper powerStatsHALWrapper) {
+ return new PowerStatsLogger(context, dataStoragePath, meterFilename,
+ modelFilename, powerStatsHALWrapper);
}
BatteryTrigger createBatteryTrigger(Context context, PowerStatsLogger powerStatsLogger) {
@@ -91,11 +100,23 @@
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- if (args.length > 0 && "--proto".equals(args[0])) {
- if (mPowerStatsLogger == null) {
- Log.e(TAG, "PowerStats HAL is not initialized. No data available.");
- } else {
- mPowerStatsLogger.writeToFile(fd);
+ if (mPowerStatsLogger == null) {
+ Slog.e(TAG, "PowerStats HAL is not initialized. No data available.");
+ } else {
+ if (args.length > 0 && "--proto".equals(args[0])) {
+ if ("model".equals(args[1])) {
+ mPowerStatsLogger.writeModelDataToFile(fd);
+ } else if ("meter".equals(args[1])) {
+ mPowerStatsLogger.writeMeterDataToFile(fd);
+ }
+ } else if (args.length == 0) {
+ pw.println("PowerStatsService dumpsys: available ChannelInfos");
+ ChannelInfo[] channelInfo = mPowerStatsHALWrapper.getEnergyMeterInfo();
+ ChannelInfoUtils.dumpsys(channelInfo, pw);
+
+ pw.println("PowerStatsService dumpsys: available EnergyConsumerIds");
+ int[] energyConsumerId = mPowerStatsHALWrapper.getEnergyConsumerInfo();
+ EnergyConsumerIdUtils.dumpsys(energyConsumerId, pw);
}
}
}
@@ -117,16 +138,16 @@
mPowerStatsHALWrapper = mInjector.createPowerStatsHALWrapperImpl();
if (mPowerStatsHALWrapper.initialize()) {
- if (DEBUG) Log.d(TAG, "Starting PowerStatsService");
+ if (DEBUG) Slog.d(TAG, "Starting PowerStatsService");
// Only start logger and triggers if initialization is successful.
mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext,
- mInjector.createDataStoragePath(), mInjector.createDataStorageFilename(),
- mPowerStatsHALWrapper);
+ mInjector.createDataStoragePath(), mInjector.createMeterFilename(),
+ mInjector.createModelFilename(), mPowerStatsHALWrapper);
mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger);
mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger);
} else {
- Log.e(TAG, "Initialization of PowerStatsHAL wrapper failed");
+ Slog.e(TAG, "Initialization of PowerStatsHAL wrapper failed");
}
}
diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
new file mode 100644
index 0000000..43afeed
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
@@ -0,0 +1,287 @@
+/*
+ * 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.powerstats;
+
+import android.hardware.power.stats.ChannelInfo;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyMeasurement;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
+import android.util.proto.WireTypeMismatchException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * ProtoStreamUtils provides helper functions for the PowerStats HAL objects returned from calls
+ * to the PowerStats HAL APIs. It provides functions to pack/unpack object arrays to/from protobuf
+ * format. These helper functions are required since frameworks code uses the genstream option
+ * when generating source code and therefore, getter/setter helper functions are not available. The
+ * protobufs need to be packed/unpacked in a more manual way using
+ * ProtoOutputStream/ProtoInputStream. It also provides print() functions for debugging purposes.
+ */
+public class ProtoStreamUtils {
+ private static final String TAG = ProtoStreamUtils.class.getSimpleName();
+
+ static class ChannelInfoUtils {
+ public static void packProtoMessage(ChannelInfo[] channelInfo, ProtoOutputStream pos) {
+ long token;
+
+ for (int i = 0; i < channelInfo.length; i++) {
+ token = pos.start(PowerStatsServiceMeterProto.CHANNEL_INFO);
+ pos.write(ChannelInfoProto.CHANNEL_ID, channelInfo[i].channelId);
+ pos.write(ChannelInfoProto.CHANNEL_NAME, channelInfo[i].channelName);
+ pos.end(token);
+ }
+
+ }
+
+ public static void print(ChannelInfo[] channelInfo) {
+ for (int i = 0; i < channelInfo.length; i++) {
+ Slog.d(TAG, "ChannelId = " + channelInfo[i].channelId
+ + ", ChannelName = " + channelInfo[i].channelName);
+ }
+ }
+
+ public static void dumpsys(ChannelInfo[] channelInfo, PrintWriter pw) {
+ for (int i = 0; i < channelInfo.length; i++) {
+ pw.println("ChannelId = " + channelInfo[i].channelId
+ + ", ChannelName = " + channelInfo[i].channelName);
+ }
+ }
+ }
+
+ static class EnergyMeasurementUtils {
+ public static byte[] getProtoBytes(EnergyMeasurement[] energyMeasurement) {
+ ProtoOutputStream pos = new ProtoOutputStream();
+ packProtoMessage(energyMeasurement, pos);
+ return pos.getBytes();
+ }
+
+ public static void packProtoMessage(EnergyMeasurement[] energyMeasurement,
+ ProtoOutputStream pos) {
+ long token;
+
+ for (int i = 0; i < energyMeasurement.length; i++) {
+ token = pos.start(PowerStatsServiceMeterProto.ENERGY_MEASUREMENT);
+ pos.write(EnergyMeasurementProto.CHANNEL_ID, energyMeasurement[i].channelId);
+ pos.write(EnergyMeasurementProto.TIMESTAMP_MS, energyMeasurement[i].timestampMs);
+ pos.write(EnergyMeasurementProto.ENERGY_UWS, energyMeasurement[i].energyUWs);
+ pos.end(token);
+ }
+ }
+
+ public static EnergyMeasurement[] unpackProtoMessage(byte[] data) throws IOException {
+ final ProtoInputStream pis = new ProtoInputStream(new ByteArrayInputStream(data));
+ List<EnergyMeasurement> energyMeasurementList = new ArrayList<EnergyMeasurement>();
+ long token;
+
+ while (true) {
+ try {
+ int nextField = pis.nextField();
+ EnergyMeasurement energyMeasurement = new EnergyMeasurement();
+
+ if (nextField == (int) PowerStatsServiceMeterProto.ENERGY_MEASUREMENT) {
+ token = pis.start(PowerStatsServiceMeterProto.ENERGY_MEASUREMENT);
+ energyMeasurementList.add(unpackProtoMessage(pis));
+ pis.end(token);
+ } else if (nextField == ProtoInputStream.NO_MORE_FIELDS) {
+ return energyMeasurementList.toArray(
+ new EnergyMeasurement[energyMeasurementList.size()]);
+ } else {
+ Slog.e(TAG, "Unhandled field in proto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ } catch (WireTypeMismatchException wtme) {
+ Slog.e(TAG, "Wire Type mismatch in proto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ }
+
+ private static EnergyMeasurement unpackProtoMessage(ProtoInputStream pis)
+ throws IOException {
+ EnergyMeasurement energyMeasurement = new EnergyMeasurement();
+
+ while (true) {
+ try {
+ switch (pis.nextField()) {
+ case (int) EnergyMeasurementProto.CHANNEL_ID:
+ energyMeasurement.channelId =
+ pis.readInt(EnergyMeasurementProto.CHANNEL_ID);
+ break;
+
+ case (int) EnergyMeasurementProto.TIMESTAMP_MS:
+ energyMeasurement.timestampMs =
+ pis.readLong(EnergyMeasurementProto.TIMESTAMP_MS);
+ break;
+
+ case (int) EnergyMeasurementProto.ENERGY_UWS:
+ energyMeasurement.energyUWs =
+ pis.readLong(EnergyMeasurementProto.ENERGY_UWS);
+ break;
+
+ case ProtoInputStream.NO_MORE_FIELDS:
+ return energyMeasurement;
+
+ default:
+ Slog.e(TAG, "Unhandled field in EnergyMeasurementProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ break;
+ }
+ } catch (WireTypeMismatchException wtme) {
+ Slog.e(TAG, "Wire Type mismatch in EnergyMeasurementProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ }
+
+ public static void print(EnergyMeasurement[] energyMeasurement) {
+ for (int i = 0; i < energyMeasurement.length; i++) {
+ Slog.d(TAG, "ChannelId = " + energyMeasurement[i].channelId
+ + ", Timestamp (ms) = " + energyMeasurement[i].timestampMs
+ + ", Energy (uWs) = " + energyMeasurement[i].energyUWs);
+ }
+ }
+ }
+
+ static class EnergyConsumerIdUtils {
+ public static void packProtoMessage(int[] energyConsumerId, ProtoOutputStream pos) {
+ long token;
+
+ for (int i = 0; i < energyConsumerId.length; i++) {
+ token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_ID);
+ pos.write(EnergyConsumerIdProto.ENERGY_CONSUMER_ID, energyConsumerId[i]);
+ pos.end(token);
+ }
+ }
+
+ public static void print(int[] energyConsumerId) {
+ for (int i = 0; i < energyConsumerId.length; i++) {
+ Slog.d(TAG, "EnergyConsumerId = " + energyConsumerId[i]);
+ }
+ }
+
+ public static void dumpsys(int[] energyConsumerId, PrintWriter pw) {
+ for (int i = 0; i < energyConsumerId.length; i++) {
+ pw.println("EnergyConsumerId = " + energyConsumerId[i]);
+ }
+ }
+ }
+
+ static class EnergyConsumerResultUtils {
+ public static byte[] getProtoBytes(EnergyConsumerResult[] energyConsumerResult) {
+ ProtoOutputStream pos = new ProtoOutputStream();
+ packProtoMessage(energyConsumerResult, pos);
+ return pos.getBytes();
+ }
+
+ public static void packProtoMessage(EnergyConsumerResult[] energyConsumerResult,
+ ProtoOutputStream pos) {
+ long token;
+
+ for (int i = 0; i < energyConsumerResult.length; i++) {
+ token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT);
+ pos.write(EnergyConsumerResultProto.ENERGY_CONSUMER_ID,
+ energyConsumerResult[i].energyConsumerId);
+ pos.write(EnergyConsumerResultProto.TIMESTAMP_MS,
+ energyConsumerResult[i].timestampMs);
+ pos.write(EnergyConsumerResultProto.ENERGY_UWS, energyConsumerResult[i].energyUWs);
+ pos.end(token);
+ }
+ }
+
+ public static EnergyConsumerResult[] unpackProtoMessage(byte[] data) throws IOException {
+ final ProtoInputStream pis = new ProtoInputStream(new ByteArrayInputStream(data));
+ List<EnergyConsumerResult> energyConsumerResultList =
+ new ArrayList<EnergyConsumerResult>();
+ long token;
+
+ while (true) {
+ try {
+ int nextField = pis.nextField();
+ EnergyConsumerResult energyConsumerResult = new EnergyConsumerResult();
+
+ if (nextField == (int) PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT) {
+ token = pis.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT);
+ energyConsumerResultList.add(unpackProtoMessage(pis));
+ pis.end(token);
+ } else if (nextField == ProtoInputStream.NO_MORE_FIELDS) {
+ return energyConsumerResultList.toArray(
+ new EnergyConsumerResult[energyConsumerResultList.size()]);
+ } else {
+ Slog.e(TAG, "Unhandled field in proto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ } catch (WireTypeMismatchException wtme) {
+ Slog.e(TAG, "Wire Type mismatch in proto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ }
+
+ private static EnergyConsumerResult unpackProtoMessage(ProtoInputStream pis)
+ throws IOException {
+ EnergyConsumerResult energyConsumerResult = new EnergyConsumerResult();
+
+ while (true) {
+ try {
+ switch (pis.nextField()) {
+ case (int) EnergyConsumerResultProto.ENERGY_CONSUMER_ID:
+ energyConsumerResult.energyConsumerId =
+ pis.readInt(EnergyConsumerResultProto.ENERGY_CONSUMER_ID);
+ break;
+
+ case (int) EnergyConsumerResultProto.TIMESTAMP_MS:
+ energyConsumerResult.timestampMs =
+ pis.readLong(EnergyConsumerResultProto.TIMESTAMP_MS);
+ break;
+
+ case (int) EnergyConsumerResultProto.ENERGY_UWS:
+ energyConsumerResult.energyUWs =
+ pis.readLong(EnergyConsumerResultProto.ENERGY_UWS);
+ break;
+
+ case ProtoInputStream.NO_MORE_FIELDS:
+ return energyConsumerResult;
+
+ default:
+ Slog.e(TAG, "Unhandled field in EnergyConsumerResultProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ break;
+ }
+ } catch (WireTypeMismatchException wtme) {
+ Slog.e(TAG, "Wire Type mismatch in EnergyConsumerResultProto: "
+ + ProtoUtils.currentFieldToString(pis));
+ }
+ }
+ }
+
+ public static void print(EnergyConsumerResult[] energyConsumerResult) {
+ for (int i = 0; i < energyConsumerResult.length; i++) {
+ Slog.d(TAG, "EnergyConsumerId = " + energyConsumerResult[i].energyConsumerId
+ + ", Timestamp (ms) = " + energyConsumerResult[i].timestampMs
+ + ", Energy (uWs) = " + energyConsumerResult[i].energyUWs);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/powerstats/TimerTrigger.java b/services/core/java/com/android/server/powerstats/TimerTrigger.java
index a9bee8b..4b59295 100644
--- a/services/core/java/com/android/server/powerstats/TimerTrigger.java
+++ b/services/core/java/com/android/server/powerstats/TimerTrigger.java
@@ -18,7 +18,7 @@
import android.content.Context;
import android.os.Handler;
-import android.util.Log;
+import android.util.Slog;
/**
* TimerTrigger sets a 60 second opportunistic timer using postDelayed.
@@ -39,7 +39,7 @@
// Do not wake the device for these messages. Opportunistically log rail data every
// LOG_PERIOD_MS.
mHandler.postDelayed(mLogData, LOG_PERIOD_MS);
- if (DEBUG) Log.d(TAG, "Received delayed message. Log rail data");
+ if (DEBUG) Slog.d(TAG, "Received delayed message. Log rail data");
logPowerStatsData();
}
};
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 f43a4ce..7433a3a 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -97,6 +97,7 @@
import android.os.IStoraged;
import android.os.IThermalEventListener;
import android.os.IThermalService;
+import android.os.OutcomeReceiver;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.RemoteException;
@@ -167,6 +168,7 @@
import com.android.server.storage.DiskStatsFileLogger;
import com.android.server.storage.DiskStatsLoggingService;
+import java.util.concurrent.ExecutionException;
import libcore.io.IoUtils;
import org.json.JSONArray;
@@ -1624,9 +1626,34 @@
int pullModemActivityInfoLocked(int atomTag, List<StatsEvent> pulledData) {
final long token = Binder.clearCallingIdentity();
try {
- SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
- mTelephony.requestModemActivityInfo(modemReceiver);
- final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
+ CompletableFuture<ModemActivityInfo> modemFuture = new CompletableFuture<>();
+ mTelephony.requestModemActivityInfo(Runnable::run,
+ new OutcomeReceiver<ModemActivityInfo,
+ TelephonyManager.ModemActivityInfoException>() {
+ @Override
+ public void onResult(ModemActivityInfo result) {
+ modemFuture.complete(result);
+ }
+
+ @Override
+ public void onError(TelephonyManager.ModemActivityInfoException e) {
+ Slog.w(TAG, "error reading modem stats:" + e);
+ modemFuture.complete(null);
+ }
+ });
+
+ ModemActivityInfo modemInfo;
+ try {
+ modemInfo = modemFuture.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
+ TimeUnit.MILLISECONDS);
+ } catch (TimeoutException | InterruptedException e) {
+ Slog.w(TAG, "timeout or interrupt reading modem stats: " + e);
+ return StatsManager.PULL_SKIP;
+ } catch (ExecutionException e) {
+ Slog.w(TAG, "exception reading modem stats: " + e.getCause());
+ return StatsManager.PULL_SKIP;
+ }
+
if (modemInfo == null) {
return StatsManager.PULL_SKIP;
}
diff --git a/services/core/java/com/android/server/testharness/TestHarnessModeService.java b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
index 5311369..52236a8 100644
--- a/services/core/java/com/android/server/testharness/TestHarnessModeService.java
+++ b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
@@ -25,7 +25,6 @@
import android.content.Intent;
import android.content.pm.UserInfo;
import android.debug.AdbManagerInternal;
-import android.debug.AdbTransportType;
import android.location.LocationManager;
import android.os.BatteryManager;
import android.os.Binder;
@@ -162,12 +161,11 @@
private void configureSettings() {
ContentResolver cr = getContext().getContentResolver();
- // Stop ADB before we enable it, otherwise on userdebug/eng builds, the keys won't have
- // registered with adbd, and it will prompt the user to confirm the keys.
- Settings.Global.putInt(cr, Settings.Global.ADB_ENABLED, 0);
- AdbManagerInternal adbManager = LocalServices.getService(AdbManagerInternal.class);
- if (adbManager.isAdbEnabled(AdbTransportType.USB)) {
- adbManager.stopAdbdForTransport(AdbTransportType.USB);
+ // If adb is already enabled, then we need to restart the daemon to pick up the change in
+ // keys. This is only really useful for userdebug/eng builds.
+ if (Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) == 1) {
+ SystemProperties.set("ctl.restart", "adbd");
+ Slog.d(TAG, "Restarted adbd");
}
// Disable the TTL for ADB keys before enabling ADB
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 363e86d..93ba758 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -66,7 +66,6 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -458,6 +457,9 @@
callback.onFailure();
} else if (serviceState.isBoundLocked()) {
if (!serviceState.checkRequestAcceptedLocked(Binder.getCallingUid(), methodName)) {
+ Slog.w(LOG_TAG, String.format("UID %d is not allowed to see the %s request",
+ Binder.getCallingUid(), methodName));
+ callback.onFailure();
return;
}
textClassifierServiceConsumer.accept(serviceState.mService);
@@ -497,7 +499,7 @@
private final IBinder mBinder;
@NonNull
private final Runnable mRequest;
- @Nullable
+ @NonNull
private final Runnable mOnServiceFailure;
@GuardedBy("mLock")
@NonNull
@@ -516,7 +518,7 @@
* @param uid the calling uid of the request.
*/
PendingRequest(@Nullable String name,
- @NonNull ThrowingRunnable request, @Nullable ThrowingRunnable onServiceFailure,
+ @NonNull ThrowingRunnable request, @NonNull ThrowingRunnable onServiceFailure,
@Nullable IBinder binder,
@NonNull TextClassificationManagerService service,
@NonNull ServiceState serviceState, int uid) {
@@ -524,7 +526,8 @@
mRequest =
logOnFailure(Objects.requireNonNull(request), "handling pending request");
mOnServiceFailure =
- logOnFailure(onServiceFailure, "notifying callback of service failure");
+ logOnFailure(Objects.requireNonNull(onServiceFailure),
+ "notifying callback of service failure");
mBinder = binder;
mService = service;
mServiceState = Objects.requireNonNull(serviceState);
@@ -799,9 +802,7 @@
request -> {
Slog.w(LOG_TAG,
String.format("Pending request[%s] is dropped", request.mName));
- if (request.mOnServiceFailure != null) {
- request.mOnServiceFailure.run();
- }
+ request.mOnServiceFailure.run();
});
@Nullable
@GuardedBy("mLock")
@@ -843,15 +844,16 @@
while ((request = mPendingRequests.poll()) != null) {
if (isBoundLocked()) {
if (!checkRequestAcceptedLocked(request.mUid, request.mName)) {
- return;
- }
- request.mRequest.run();
- } else {
- if (request.mOnServiceFailure != null) {
- Slog.d(LOG_TAG, "Unable to bind TextClassifierService for PendingRequest "
- + request.mName);
+ Slog.w(LOG_TAG, String.format("UID %d is not allowed to see the %s request",
+ request.mUid, request.mName));
request.mOnServiceFailure.run();
+ } else {
+ request.mRequest.run();
}
+ } else {
+ Slog.d(LOG_TAG, "Unable to bind TextClassifierService for PendingRequest "
+ + request.mName);
+ request.mOnServiceFailure.run();
}
if (request.mBinder != null) {
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 2018940..68f554c 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -197,19 +197,6 @@
}
@Override
- public boolean isFallbackLogicEnabled() {
- // Note that this is enabled by default (i.e. if the setting hasn't been set).
- return Settings.Global.getInt(AppGlobals.getInitialApplication().getContentResolver(),
- Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, 1) == 1;
- }
-
- @Override
- public void enableFallbackLogic(boolean enable) {
- Settings.Global.putInt(AppGlobals.getInitialApplication().getContentResolver(),
- Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, enable ? 1 : 0);
- }
-
- @Override
public void enablePackageForAllUsers(Context context, String packageName, boolean enable) {
UserManager userManager = (UserManager)context.getSystemService(Context.USER_SERVICE);
for(UserInfo userInfo : userManager.getUsers()) {
diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java
index 743740d..09c23a7 100644
--- a/services/core/java/com/android/server/webkit/SystemInterface.java
+++ b/services/core/java/com/android/server/webkit/SystemInterface.java
@@ -41,9 +41,6 @@
public void updateUserSetting(Context context, String newProviderName);
public void killPackageDependents(String packageName);
- public boolean isFallbackLogicEnabled();
- public void enableFallbackLogic(boolean enable);
-
public void enablePackageForAllUsers(Context context, String packageName, boolean enable);
public boolean systemIsDebuggable();
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 11fd795..4d670f1 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -15,15 +15,22 @@
*/
package com.android.server.webkit;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.Signature;
import android.os.AsyncTask;
import android.os.UserHandle;
import android.util.Slog;
+import android.webkit.UserPackage;
+import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
/**
* Implementation of the WebViewUpdateService.
@@ -33,18 +40,17 @@
* This class keeps track of and prepares the current WebView implementation, and needs to keep
* track of a couple of different things such as what package is used as WebView implementation.
*
- * The public methods in this class are accessed from WebViewUpdateService either on the UI thread
- * or on one of multiple Binder threads. The WebView preparation code shares state between threads
- * meaning that code that chooses a new WebView implementation or checks which implementation is
- * being used needs to hold a lock.
+ * The package-visible methods in this class are accessed from WebViewUpdateService either on the UI
+ * thread or on one of multiple Binder threads. The WebView preparation code shares state between
+ * threads meaning that code that chooses a new WebView implementation or checks which
+ * implementation is being used needs to hold a lock.
*
* The WebViewUpdateService can be accessed in a couple of different ways.
* 1. It is started from the SystemServer at boot - at that point we just initiate some state such
* as the WebView preparation class.
* 2. The SystemServer calls WebViewUpdateService.prepareWebViewInSystemServer. This happens at boot
* and the WebViewUpdateService should not have been accessed before this call. In this call we
- * migrate away from the old fallback logic if necessary and then choose WebView implementation for
- * the first time.
+ * choose WebView implementation for the first time.
* 3. The update service listens for Intents related to package installs and removals. These intents
* are received and processed on the UI thread. Each intent can result in changing WebView
* implementation.
@@ -56,37 +62,133 @@
*
* @hide
*/
-public class WebViewUpdateServiceImpl {
+class WebViewUpdateServiceImpl {
private static final String TAG = WebViewUpdateServiceImpl.class.getSimpleName();
- private SystemInterface mSystemInterface;
- private WebViewUpdater mWebViewUpdater;
- final private Context mContext;
+ private static class WebViewPackageMissingException extends Exception {
+ WebViewPackageMissingException(String message) {
+ super(message);
+ }
- private final static int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE;
- private final static int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE;
+ WebViewPackageMissingException(Exception e) {
+ super(e);
+ }
+ }
- public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) {
+ private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000.
+ private static final long NS_PER_MS = 1000000;
+
+ private static final int VALIDITY_OK = 0;
+ private static final int VALIDITY_INCORRECT_SDK_VERSION = 1;
+ private static final int VALIDITY_INCORRECT_VERSION_CODE = 2;
+ private static final int VALIDITY_INCORRECT_SIGNATURE = 3;
+ private static final int VALIDITY_NO_LIBRARY_FLAG = 4;
+
+ private static final int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE;
+ private static final int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE;
+
+ private final SystemInterface mSystemInterface;
+ private final Context mContext;
+
+ private long mMinimumVersionCode = -1;
+
+ // Keeps track of the number of running relro creations
+ private int mNumRelroCreationsStarted = 0;
+ private int mNumRelroCreationsFinished = 0;
+ // Implies that we need to rerun relro creation because we are using an out-of-date package
+ private boolean mWebViewPackageDirty = false;
+ private boolean mAnyWebViewInstalled = false;
+
+ private static final int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
+
+ // The WebView package currently in use (or the one we are preparing).
+ private PackageInfo mCurrentWebViewPackage = null;
+
+ private final Object mLock = new Object();
+
+ WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) {
mContext = context;
mSystemInterface = systemInterface;
- mWebViewUpdater = new WebViewUpdater(mContext, mSystemInterface);
}
void packageStateChanged(String packageName, int changedState, int userId) {
// We don't early out here in different cases where we could potentially early-out (e.g. if
// we receive PACKAGE_CHANGED for another user than the system user) since that would
// complicate this logic further and open up for more edge cases.
- mWebViewUpdater.packageStateChanged(packageName, changedState);
+ for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
+ String webviewPackage = provider.packageName;
+
+ if (webviewPackage.equals(packageName)) {
+ boolean updateWebView = false;
+ boolean removedOrChangedOldPackage = false;
+ String oldProviderName = null;
+ PackageInfo newPackage = null;
+ synchronized (mLock) {
+ try {
+ newPackage = findPreferredWebViewPackage();
+ if (mCurrentWebViewPackage != null) {
+ oldProviderName = mCurrentWebViewPackage.packageName;
+ }
+ // Only trigger update actions if the updated package is the one
+ // that will be used, or the one that was in use before the
+ // update, or if we haven't seen a valid WebView package before.
+ updateWebView =
+ provider.packageName.equals(newPackage.packageName)
+ || provider.packageName.equals(oldProviderName)
+ || mCurrentWebViewPackage == null;
+ // We removed the old package if we received an intent to remove
+ // or replace the old package.
+ removedOrChangedOldPackage =
+ provider.packageName.equals(oldProviderName);
+ if (updateWebView) {
+ onWebViewProviderChanged(newPackage);
+ }
+ } catch (WebViewPackageMissingException e) {
+ mCurrentWebViewPackage = null;
+ Slog.e(TAG, "Could not find valid WebView package to create relro with "
+ + e);
+ }
+ }
+ if (updateWebView && !removedOrChangedOldPackage
+ && oldProviderName != null) {
+ // If the provider change is the result of adding or replacing a
+ // package that was not the previous provider then we must kill
+ // packages dependent on the old package ourselves. The framework
+ // only kills dependents of packages that are being removed.
+ mSystemInterface.killPackageDependents(oldProviderName);
+ }
+ return;
+ }
+ }
}
void prepareWebViewInSystemServer() {
- migrateFallbackStateOnBoot();
- mWebViewUpdater.prepareWebViewInSystemServer();
+ try {
+ synchronized (mLock) {
+ mCurrentWebViewPackage = findPreferredWebViewPackage();
+ String userSetting = mSystemInterface.getUserChosenWebViewProvider(mContext);
+ if (userSetting != null
+ && !userSetting.equals(mCurrentWebViewPackage.packageName)) {
+ // Don't persist the user-chosen setting across boots if the package being
+ // chosen is not used (could be disabled or uninstalled) so that the user won't
+ // be surprised by the device switching to using a certain webview package,
+ // that was uninstalled/disabled a long time ago, if it is installed/enabled
+ // again.
+ mSystemInterface.updateUserSetting(mContext,
+ mCurrentWebViewPackage.packageName);
+ }
+ onWebViewProviderChanged(mCurrentWebViewPackage);
+ }
+ } catch (Throwable t) {
+ // Log and discard errors at this stage as we must not crash the system server.
+ Slog.e(TAG, "error preparing webview provider from system server", t);
+ }
+
if (getCurrentWebViewPackage() == null) {
// We didn't find a valid WebView implementation. Try explicitly re-enabling the
// fallback package for all users in case it was disabled, even if we already did the
- // one-time migration before. If this actually changes the state, WebViewUpdater will
- // see the PackageManager broadcast shortly and try again.
+ // one-time migration before. If this actually changes the state, we will see the
+ // PackageManager broadcast shortly and try again.
WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
if (fallbackProvider != null) {
@@ -105,7 +207,7 @@
}
}
- void startZygoteWhenReady() {
+ private void startZygoteWhenReady() {
// Wait on a background thread for RELRO creation to be done. We ignore the return value
// because even if RELRO creation failed we still want to start the zygote.
waitForAndGetProvider();
@@ -131,23 +233,231 @@
*/
private void handleUserChange() {
// Potentially trigger package-changing logic.
- mWebViewUpdater.updateCurrentWebViewPackage(null);
+ updateCurrentWebViewPackage(null);
}
void notifyRelroCreationCompleted() {
- mWebViewUpdater.notifyRelroCreationCompleted();
+ synchronized (mLock) {
+ mNumRelroCreationsFinished++;
+ checkIfRelrosDoneLocked();
+ }
}
WebViewProviderResponse waitForAndGetProvider() {
- return mWebViewUpdater.waitForAndGetProvider();
+ PackageInfo webViewPackage = null;
+ final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
+ boolean webViewReady = false;
+ int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
+ synchronized (mLock) {
+ webViewReady = webViewIsReadyLocked();
+ while (!webViewReady) {
+ final long timeNowMs = System.nanoTime() / NS_PER_MS;
+ if (timeNowMs >= timeoutTimeMs) break;
+ try {
+ mLock.wait(timeoutTimeMs - timeNowMs);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ webViewReady = webViewIsReadyLocked();
+ }
+ // Make sure we return the provider that was used to create the relro file
+ webViewPackage = mCurrentWebViewPackage;
+ if (webViewReady) {
+ // success
+ } else if (!mAnyWebViewInstalled) {
+ webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
+ } else {
+ // Either the current relro creation isn't done yet, or the new relro creatioin
+ // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
+ webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
+ Slog.e(TAG, "Timed out waiting for relro creation, relros started "
+ + mNumRelroCreationsStarted
+ + " relros finished " + mNumRelroCreationsFinished
+ + " package dirty? " + mWebViewPackageDirty);
+ }
+ }
+ if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
+ return new WebViewProviderResponse(webViewPackage, webViewStatus);
}
- String changeProviderAndSetting(String newProvider) {
- return mWebViewUpdater.changeProviderAndSetting(newProvider);
+ /**
+ * Change WebView provider and provider setting and kill packages using the old provider.
+ * Return the new provider (in case we are in the middle of creating relro files, or
+ * replacing that provider it will not be in use directly, but will be used when the relros
+ * or the replacement are done).
+ */
+ String changeProviderAndSetting(String newProviderName) {
+ PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName);
+ if (newPackage == null) return "";
+ return newPackage.packageName;
}
+ /**
+ * Update the current WebView package.
+ * @param newProviderName the package to switch to, null if no package has been explicitly
+ * chosen.
+ */
+ private PackageInfo updateCurrentWebViewPackage(@Nullable String newProviderName) {
+ PackageInfo oldPackage = null;
+ PackageInfo newPackage = null;
+ boolean providerChanged = false;
+ synchronized (mLock) {
+ oldPackage = mCurrentWebViewPackage;
+
+ if (newProviderName != null) {
+ mSystemInterface.updateUserSetting(mContext, newProviderName);
+ }
+
+ try {
+ newPackage = findPreferredWebViewPackage();
+ providerChanged = (oldPackage == null)
+ || !newPackage.packageName.equals(oldPackage.packageName);
+ } catch (WebViewPackageMissingException e) {
+ // If updated the Setting but don't have an installed WebView package, the
+ // Setting will be used when a package is available.
+ mCurrentWebViewPackage = null;
+ Slog.e(TAG, "Couldn't find WebView package to use " + e);
+ return null;
+ }
+ // Perform the provider change if we chose a new provider
+ if (providerChanged) {
+ onWebViewProviderChanged(newPackage);
+ }
+ }
+ // Kill apps using the old provider only if we changed provider
+ if (providerChanged && oldPackage != null) {
+ mSystemInterface.killPackageDependents(oldPackage.packageName);
+ }
+ // Return the new provider, this is not necessarily the one we were asked to switch to,
+ // but the persistent setting will now be pointing to the provider we were asked to
+ // switch to anyway.
+ return newPackage;
+ }
+
+ /**
+ * This is called when we change WebView provider, either when the current provider is
+ * updated or a new provider is chosen / takes precedence.
+ */
+ private void onWebViewProviderChanged(PackageInfo newPackage) {
+ synchronized (mLock) {
+ mAnyWebViewInstalled = true;
+ if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
+ mCurrentWebViewPackage = newPackage;
+
+ // The relro creations might 'finish' (not start at all) before
+ // WebViewFactory.onWebViewProviderChanged which means we might not know the
+ // number of started creations before they finish.
+ mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
+ mNumRelroCreationsFinished = 0;
+ mNumRelroCreationsStarted =
+ mSystemInterface.onWebViewProviderChanged(newPackage);
+ // If the relro creations finish before we know the number of started creations
+ // we will have to do any cleanup/notifying here.
+ checkIfRelrosDoneLocked();
+ } else {
+ mWebViewPackageDirty = true;
+ }
+ }
+ }
+
+ /**
+ * Fetch only the currently valid WebView packages.
+ **/
WebViewProviderInfo[] getValidWebViewPackages() {
- return mWebViewUpdater.getValidWebViewPackages();
+ ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
+ WebViewProviderInfo[] providers =
+ new WebViewProviderInfo[providersAndPackageInfos.length];
+ for (int n = 0; n < providersAndPackageInfos.length; n++) {
+ providers[n] = providersAndPackageInfos[n].provider;
+ }
+ return providers;
+ }
+
+ private static class ProviderAndPackageInfo {
+ public final WebViewProviderInfo provider;
+ public final PackageInfo packageInfo;
+
+ ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) {
+ this.provider = provider;
+ this.packageInfo = packageInfo;
+ }
+ }
+
+ private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
+ WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
+ List<ProviderAndPackageInfo> providers = new ArrayList<>();
+ for (int n = 0; n < allProviders.length; n++) {
+ try {
+ PackageInfo packageInfo =
+ mSystemInterface.getPackageInfoForProvider(allProviders[n]);
+ if (validityResult(allProviders[n], packageInfo) == VALIDITY_OK) {
+ providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
+ }
+ } catch (NameNotFoundException e) {
+ // Don't add non-existent packages
+ }
+ }
+ return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
+ }
+
+ /**
+ * Returns either the package info of the WebView provider determined in the following way:
+ * If the user has chosen a provider then use that if it is valid,
+ * otherwise use the first package in the webview priority list that is valid.
+ *
+ */
+ private PackageInfo findPreferredWebViewPackage() throws WebViewPackageMissingException {
+ ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
+
+ String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext);
+
+ // If the user has chosen provider, use that (if it's installed and enabled for all
+ // users).
+ for (ProviderAndPackageInfo providerAndPackage : providers) {
+ if (providerAndPackage.provider.packageName.equals(userChosenProvider)) {
+ // userPackages can contain null objects.
+ List<UserPackage> userPackages =
+ mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
+ providerAndPackage.provider);
+ if (isInstalledAndEnabledForAllUsers(userPackages)) {
+ return providerAndPackage.packageInfo;
+ }
+ }
+ }
+
+ // User did not choose, or the choice failed; use the most stable provider that is
+ // installed and enabled for all users, and available by default (not through
+ // user choice).
+ for (ProviderAndPackageInfo providerAndPackage : providers) {
+ if (providerAndPackage.provider.availableByDefault) {
+ // userPackages can contain null objects.
+ List<UserPackage> userPackages =
+ mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
+ providerAndPackage.provider);
+ if (isInstalledAndEnabledForAllUsers(userPackages)) {
+ return providerAndPackage.packageInfo;
+ }
+ }
+ }
+
+ // This should never happen during normal operation (only with modified system images).
+ mAnyWebViewInstalled = false;
+ throw new WebViewPackageMissingException("Could not find a loadable WebView package");
+ }
+
+ /**
+ * Return true iff {@param packageInfos} point to only installed and enabled packages.
+ * The given packages {@param packageInfos} should all be pointing to the same package, but each
+ * PackageInfo representing a different user's package.
+ */
+ private static boolean isInstalledAndEnabledForAllUsers(
+ List<UserPackage> userPackages) {
+ for (UserPackage userPackage : userPackages) {
+ if (!userPackage.isInstalledPackage() || !userPackage.isEnabledPackage()) {
+ return false;
+ }
+ }
+ return true;
}
WebViewProviderInfo[] getWebViewPackages() {
@@ -155,28 +465,143 @@
}
PackageInfo getCurrentWebViewPackage() {
- return mWebViewUpdater.getCurrentWebViewPackage();
+ synchronized (mLock) {
+ return mCurrentWebViewPackage;
+ }
}
/**
- * If the fallback logic is enabled, re-enable any fallback package for all users, then
- * disable the fallback logic.
- *
- * This migrates away from the old fallback mechanism to the new state where packages are never
- * automatically enableenableisabled.
+ * Returns whether WebView is ready and is not going to go through its preparation phase
+ * again directly.
*/
- private void migrateFallbackStateOnBoot() {
- if (!mSystemInterface.isFallbackLogicEnabled()) return;
+ private boolean webViewIsReadyLocked() {
+ return !mWebViewPackageDirty
+ && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
+ // The current package might be replaced though we haven't received an intent
+ // declaring this yet, the following flag makes anyone loading WebView to wait in
+ // this case.
+ && mAnyWebViewInstalled;
+ }
- WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
- WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
- if (fallbackProvider != null) {
- Slog.i(TAG, "One-time migration: enabling " + fallbackProvider.packageName);
- mSystemInterface.enablePackageForAllUsers(mContext, fallbackProvider.packageName, true);
- } else {
- Slog.i(TAG, "Skipping one-time migration: no fallback provider");
+ private void checkIfRelrosDoneLocked() {
+ if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
+ if (mWebViewPackageDirty) {
+ mWebViewPackageDirty = false;
+ // If we have changed provider since we started the relro creation we need to
+ // redo the whole process using the new package instead.
+ try {
+ PackageInfo newPackage = findPreferredWebViewPackage();
+ onWebViewProviderChanged(newPackage);
+ } catch (WebViewPackageMissingException e) {
+ mCurrentWebViewPackage = null;
+ // If we can't find any valid WebView package we are now in a state where
+ // mAnyWebViewInstalled is false, so loading WebView will be blocked and we
+ // should simply wait until we receive an intent declaring a new package was
+ // installed.
+ }
+ } else {
+ mLock.notifyAll();
+ }
}
- mSystemInterface.enableFallbackLogic(false);
+ }
+
+ private int validityResult(WebViewProviderInfo configInfo, PackageInfo packageInfo) {
+ // Ensure the provider targets this framework release (or a later one).
+ if (!UserPackage.hasCorrectTargetSdkVersion(packageInfo)) {
+ return VALIDITY_INCORRECT_SDK_VERSION;
+ }
+ if (!versionCodeGE(packageInfo.getLongVersionCode(), getMinimumVersionCode())
+ && !mSystemInterface.systemIsDebuggable()) {
+ // Webview providers may be downgraded arbitrarily low, prevent that by enforcing
+ // minimum version code. This check is only enforced for user builds.
+ return VALIDITY_INCORRECT_VERSION_CODE;
+ }
+ if (!providerHasValidSignature(configInfo, packageInfo, mSystemInterface)) {
+ return VALIDITY_INCORRECT_SIGNATURE;
+ }
+ if (WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) == null) {
+ return VALIDITY_NO_LIBRARY_FLAG;
+ }
+ return VALIDITY_OK;
+ }
+
+ /**
+ * Both versionCodes should be from a WebView provider package implemented by Chromium.
+ * VersionCodes from other kinds of packages won't make any sense in this method.
+ *
+ * An introduction to Chromium versionCode scheme:
+ * "BBBBPPPXX"
+ * BBBB: 4 digit branch number. It monotonically increases over time.
+ * PPP: patch number in the branch. It is padded with zeroes to the left. These three digits
+ * may change their meaning in the future.
+ * XX: Digits to differentiate different APK builds of the same source version.
+ *
+ * This method takes the "BBBB" of versionCodes and compare them.
+ *
+ * https://www.chromium.org/developers/version-numbers describes general Chromium versioning;
+ * https://source.chromium.org/chromium/chromium/src/+/master:build/util/android_chrome_version.py
+ * is the canonical source for how Chromium versionCodes are calculated.
+ *
+ * @return true if versionCode1 is higher than or equal to versionCode2.
+ */
+ private static boolean versionCodeGE(long versionCode1, long versionCode2) {
+ long v1 = versionCode1 / 100000;
+ long v2 = versionCode2 / 100000;
+
+ return v1 >= v2;
+ }
+
+ /**
+ * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode
+ * of all available-by-default WebView provider packages. If there is no such WebView provider
+ * package on the system, then return -1, which means all positive versionCode WebView packages
+ * are accepted.
+ *
+ * Note that this is a private method that handles a variable (mMinimumVersionCode) which is
+ * shared between threads. Furthermore, this method does not hold mLock meaning that we must
+ * take extra care to ensure this method is thread-safe.
+ */
+ private long getMinimumVersionCode() {
+ if (mMinimumVersionCode > 0) {
+ return mMinimumVersionCode;
+ }
+
+ long minimumVersionCode = -1;
+ for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
+ if (provider.availableByDefault) {
+ try {
+ long versionCode =
+ mSystemInterface.getFactoryPackageVersion(provider.packageName);
+ if (minimumVersionCode < 0 || versionCode < minimumVersionCode) {
+ minimumVersionCode = versionCode;
+ }
+ } catch (NameNotFoundException e) {
+ // Safe to ignore.
+ }
+ }
+ }
+
+ mMinimumVersionCode = minimumVersionCode;
+ return mMinimumVersionCode;
+ }
+
+ private static boolean providerHasValidSignature(WebViewProviderInfo provider,
+ PackageInfo packageInfo, SystemInterface systemInterface) {
+ // Skip checking signatures on debuggable builds, for development purposes.
+ if (systemInterface.systemIsDebuggable()) return true;
+
+ // Allow system apps to be valid providers regardless of signature.
+ if (packageInfo.applicationInfo.isSystemApp()) return true;
+
+ // We don't support packages with multiple signatures.
+ if (packageInfo.signatures.length != 1) return false;
+
+ // If any of the declared signatures match the package signature, it's valid.
+ for (Signature signature : provider.signatures) {
+ if (signature.equals(packageInfo.signatures[0])) return true;
+ }
+
+ return false;
}
/**
@@ -217,9 +642,89 @@
*/
void dumpState(PrintWriter pw) {
pw.println("Current WebView Update Service state");
- pw.println(String.format(" Fallback logic enabled: %b",
- mSystemInterface.isFallbackLogicEnabled()));
pw.println(String.format(" Multiprocess enabled: %b", isMultiProcessEnabled()));
- mWebViewUpdater.dumpState(pw);
+ synchronized (mLock) {
+ if (mCurrentWebViewPackage == null) {
+ pw.println(" Current WebView package is null");
+ } else {
+ pw.println(String.format(" Current WebView package (name, version): (%s, %s)",
+ mCurrentWebViewPackage.packageName,
+ mCurrentWebViewPackage.versionName));
+ }
+ pw.println(String.format(" Minimum targetSdkVersion: %d",
+ UserPackage.MINIMUM_SUPPORTED_SDK));
+ pw.println(String.format(" Minimum WebView version code: %d",
+ mMinimumVersionCode));
+ pw.println(String.format(" Number of relros started: %d",
+ mNumRelroCreationsStarted));
+ pw.println(String.format(" Number of relros finished: %d",
+ mNumRelroCreationsFinished));
+ pw.println(String.format(" WebView package dirty: %b", mWebViewPackageDirty));
+ pw.println(String.format(" Any WebView package installed: %b",
+ mAnyWebViewInstalled));
+
+ try {
+ PackageInfo preferredWebViewPackage = findPreferredWebViewPackage();
+ pw.println(String.format(
+ " Preferred WebView package (name, version): (%s, %s)",
+ preferredWebViewPackage.packageName,
+ preferredWebViewPackage.versionName));
+ } catch (WebViewPackageMissingException e) {
+ pw.println(String.format(" Preferred WebView package: none"));
+ }
+
+ dumpAllPackageInformationLocked(pw);
+ }
+ }
+
+ private void dumpAllPackageInformationLocked(PrintWriter pw) {
+ WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
+ pw.println(" WebView packages:");
+ for (WebViewProviderInfo provider : allProviders) {
+ List<UserPackage> userPackages =
+ mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider);
+ PackageInfo systemUserPackageInfo =
+ userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo();
+ if (systemUserPackageInfo == null) {
+ pw.println(String.format(" %s is NOT installed.", provider.packageName));
+ continue;
+ }
+
+ int validity = validityResult(provider, systemUserPackageInfo);
+ String packageDetails = String.format(
+ "versionName: %s, versionCode: %d, targetSdkVersion: %d",
+ systemUserPackageInfo.versionName,
+ systemUserPackageInfo.getLongVersionCode(),
+ systemUserPackageInfo.applicationInfo.targetSdkVersion);
+ if (validity == VALIDITY_OK) {
+ boolean installedForAllUsers = isInstalledAndEnabledForAllUsers(
+ mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider));
+ pw.println(String.format(
+ " Valid package %s (%s) is %s installed/enabled for all users",
+ systemUserPackageInfo.packageName,
+ packageDetails,
+ installedForAllUsers ? "" : "NOT"));
+ } else {
+ pw.println(String.format(" Invalid package %s (%s), reason: %s",
+ systemUserPackageInfo.packageName,
+ packageDetails,
+ getInvalidityReason(validity)));
+ }
+ }
+ }
+
+ private static String getInvalidityReason(int invalidityReason) {
+ switch (invalidityReason) {
+ case VALIDITY_INCORRECT_SDK_VERSION:
+ return "SDK version too low";
+ case VALIDITY_INCORRECT_VERSION_CODE:
+ return "Version code too low";
+ case VALIDITY_INCORRECT_SIGNATURE:
+ return "Incorrect signature";
+ case VALIDITY_NO_LIBRARY_FLAG:
+ return "No WebView-library manifest flag";
+ default:
+ return "Unexcepted validity-reason";
+ }
}
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdater.java b/services/core/java/com/android/server/webkit/WebViewUpdater.java
deleted file mode 100644
index 3b58af2..0000000
--- a/services/core/java/com/android/server/webkit/WebViewUpdater.java
+++ /dev/null
@@ -1,599 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.webkit;
-
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.Signature;
-import android.os.UserHandle;
-import android.util.Slog;
-import android.webkit.UserPackage;
-import android.webkit.WebViewFactory;
-import android.webkit.WebViewProviderInfo;
-import android.webkit.WebViewProviderResponse;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class that decides what WebView implementation to use and prepares that implementation for
- * use.
- */
-class WebViewUpdater {
- private static final String TAG = WebViewUpdater.class.getSimpleName();
-
- private static class WebViewPackageMissingException extends Exception {
- public WebViewPackageMissingException(String message) { super(message); }
- public WebViewPackageMissingException(Exception e) { super(e); }
- }
-
- private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000.
-
- private final static int VALIDITY_OK = 0;
- private final static int VALIDITY_INCORRECT_SDK_VERSION = 1;
- private final static int VALIDITY_INCORRECT_VERSION_CODE = 2;
- private final static int VALIDITY_INCORRECT_SIGNATURE = 3;
- private final static int VALIDITY_NO_LIBRARY_FLAG = 4;
-
- private Context mContext;
- private SystemInterface mSystemInterface;
- private long mMinimumVersionCode = -1;
-
- // Keeps track of the number of running relro creations
- private int mNumRelroCreationsStarted = 0;
- private int mNumRelroCreationsFinished = 0;
- // Implies that we need to rerun relro creation because we are using an out-of-date package
- private boolean mWebViewPackageDirty = false;
- private boolean mAnyWebViewInstalled = false;
-
- private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
-
- // The WebView package currently in use (or the one we are preparing).
- private PackageInfo mCurrentWebViewPackage = null;
-
- private final Object mLock = new Object();
-
- WebViewUpdater(Context context, SystemInterface systemInterface) {
- mContext = context;
- mSystemInterface = systemInterface;
- }
-
- void packageStateChanged(String packageName, int changedState) {
- for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
- String webviewPackage = provider.packageName;
-
- if (webviewPackage.equals(packageName)) {
- boolean updateWebView = false;
- boolean removedOrChangedOldPackage = false;
- String oldProviderName = null;
- PackageInfo newPackage = null;
- synchronized(mLock) {
- try {
- newPackage = findPreferredWebViewPackage();
- if (mCurrentWebViewPackage != null) {
- oldProviderName = mCurrentWebViewPackage.packageName;
- }
- // Only trigger update actions if the updated package is the one
- // that will be used, or the one that was in use before the
- // update, or if we haven't seen a valid WebView package before.
- updateWebView =
- provider.packageName.equals(newPackage.packageName)
- || provider.packageName.equals(oldProviderName)
- || mCurrentWebViewPackage == null;
- // We removed the old package if we received an intent to remove
- // or replace the old package.
- removedOrChangedOldPackage =
- provider.packageName.equals(oldProviderName);
- if (updateWebView) {
- onWebViewProviderChanged(newPackage);
- }
- } catch (WebViewPackageMissingException e) {
- mCurrentWebViewPackage = null;
- Slog.e(TAG, "Could not find valid WebView package to create " +
- "relro with " + e);
- }
- }
- if(updateWebView && !removedOrChangedOldPackage
- && oldProviderName != null) {
- // If the provider change is the result of adding or replacing a
- // package that was not the previous provider then we must kill
- // packages dependent on the old package ourselves. The framework
- // only kills dependents of packages that are being removed.
- mSystemInterface.killPackageDependents(oldProviderName);
- }
- return;
- }
- }
- }
-
- void prepareWebViewInSystemServer() {
- try {
- synchronized(mLock) {
- mCurrentWebViewPackage = findPreferredWebViewPackage();
- String userSetting = mSystemInterface.getUserChosenWebViewProvider(mContext);
- if (userSetting != null
- && !userSetting.equals(mCurrentWebViewPackage.packageName)) {
- // Don't persist the user-chosen setting across boots if the package being
- // chosen is not used (could be disabled or uninstalled) so that the user won't
- // be surprised by the device switching to using a certain webview package,
- // that was uninstalled/disabled a long time ago, if it is installed/enabled
- // again.
- mSystemInterface.updateUserSetting(mContext,
- mCurrentWebViewPackage.packageName);
- }
- onWebViewProviderChanged(mCurrentWebViewPackage);
- }
- } catch (Throwable t) {
- // Log and discard errors at this stage as we must not crash the system server.
- Slog.e(TAG, "error preparing webview provider from system server", t);
- }
- }
-
- /**
- * Change WebView provider and provider setting and kill packages using the old provider.
- * Return the new provider (in case we are in the middle of creating relro files, or
- * replacing that provider it will not be in use directly, but will be used when the relros
- * or the replacement are done).
- */
- String changeProviderAndSetting(String newProviderName) {
- PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName);
- if (newPackage == null) return "";
- return newPackage.packageName;
- }
-
- /**
- * Update the current WebView package.
- * @param newProviderName the package to switch to, null if no package has been explicitly
- * chosen.
- */
- PackageInfo updateCurrentWebViewPackage(String newProviderName) {
- PackageInfo oldPackage = null;
- PackageInfo newPackage = null;
- boolean providerChanged = false;
- synchronized(mLock) {
- oldPackage = mCurrentWebViewPackage;
-
- if (newProviderName != null) {
- mSystemInterface.updateUserSetting(mContext, newProviderName);
- }
-
- try {
- newPackage = findPreferredWebViewPackage();
- providerChanged = (oldPackage == null)
- || !newPackage.packageName.equals(oldPackage.packageName);
- } catch (WebViewPackageMissingException e) {
- // If updated the Setting but don't have an installed WebView package, the
- // Setting will be used when a package is available.
- mCurrentWebViewPackage = null;
- Slog.e(TAG, "Couldn't find WebView package to use " + e);
- return null;
- }
- // Perform the provider change if we chose a new provider
- if (providerChanged) {
- onWebViewProviderChanged(newPackage);
- }
- }
- // Kill apps using the old provider only if we changed provider
- if (providerChanged && oldPackage != null) {
- mSystemInterface.killPackageDependents(oldPackage.packageName);
- }
- // Return the new provider, this is not necessarily the one we were asked to switch to,
- // but the persistent setting will now be pointing to the provider we were asked to
- // switch to anyway.
- return newPackage;
- }
-
- /**
- * This is called when we change WebView provider, either when the current provider is
- * updated or a new provider is chosen / takes precedence.
- */
- private void onWebViewProviderChanged(PackageInfo newPackage) {
- synchronized(mLock) {
- mAnyWebViewInstalled = true;
- if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
- mCurrentWebViewPackage = newPackage;
-
- // The relro creations might 'finish' (not start at all) before
- // WebViewFactory.onWebViewProviderChanged which means we might not know the
- // number of started creations before they finish.
- mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
- mNumRelroCreationsFinished = 0;
- mNumRelroCreationsStarted =
- mSystemInterface.onWebViewProviderChanged(newPackage);
- // If the relro creations finish before we know the number of started creations
- // we will have to do any cleanup/notifying here.
- checkIfRelrosDoneLocked();
- } else {
- mWebViewPackageDirty = true;
- }
- }
- }
-
- /**
- * Fetch only the currently valid WebView packages.
- **/
- WebViewProviderInfo[] getValidWebViewPackages() {
- ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
- WebViewProviderInfo[] providers =
- new WebViewProviderInfo[providersAndPackageInfos.length];
- for(int n = 0; n < providersAndPackageInfos.length; n++) {
- providers[n] = providersAndPackageInfos[n].provider;
- }
- return providers;
- }
-
- private static class ProviderAndPackageInfo {
- public final WebViewProviderInfo provider;
- public final PackageInfo packageInfo;
-
- public ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) {
- this.provider = provider;
- this.packageInfo = packageInfo;
- }
- }
-
- private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
- WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
- List<ProviderAndPackageInfo> providers = new ArrayList<>();
- for(int n = 0; n < allProviders.length; n++) {
- try {
- PackageInfo packageInfo =
- mSystemInterface.getPackageInfoForProvider(allProviders[n]);
- if (isValidProvider(allProviders[n], packageInfo)) {
- providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
- }
- } catch (NameNotFoundException e) {
- // Don't add non-existent packages
- }
- }
- return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
- }
-
- /**
- * Returns either the package info of the WebView provider determined in the following way:
- * If the user has chosen a provider then use that if it is valid,
- * otherwise use the first package in the webview priority list that is valid.
- *
- */
- private PackageInfo findPreferredWebViewPackage() throws WebViewPackageMissingException {
- ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
-
- String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext);
-
- // If the user has chosen provider, use that (if it's installed and enabled for all
- // users).
- for (ProviderAndPackageInfo providerAndPackage : providers) {
- if (providerAndPackage.provider.packageName.equals(userChosenProvider)) {
- // userPackages can contain null objects.
- List<UserPackage> userPackages =
- mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
- providerAndPackage.provider);
- if (isInstalledAndEnabledForAllUsers(userPackages)) {
- return providerAndPackage.packageInfo;
- }
- }
- }
-
- // User did not choose, or the choice failed; use the most stable provider that is
- // installed and enabled for all users, and available by default (not through
- // user choice).
- for (ProviderAndPackageInfo providerAndPackage : providers) {
- if (providerAndPackage.provider.availableByDefault) {
- // userPackages can contain null objects.
- List<UserPackage> userPackages =
- mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
- providerAndPackage.provider);
- if (isInstalledAndEnabledForAllUsers(userPackages)) {
- return providerAndPackage.packageInfo;
- }
- }
- }
-
- // This should never happen during normal operation (only with modified system images).
- mAnyWebViewInstalled = false;
- throw new WebViewPackageMissingException("Could not find a loadable WebView package");
- }
-
- /**
- * Return true iff {@param packageInfos} point to only installed and enabled packages.
- * The given packages {@param packageInfos} should all be pointing to the same package, but each
- * PackageInfo representing a different user's package.
- */
- static boolean isInstalledAndEnabledForAllUsers(
- List<UserPackage> userPackages) {
- for (UserPackage userPackage : userPackages) {
- if (!userPackage.isInstalledPackage() || !userPackage.isEnabledPackage()) {
- return false;
- }
- }
- return true;
- }
-
- void notifyRelroCreationCompleted() {
- synchronized (mLock) {
- mNumRelroCreationsFinished++;
- checkIfRelrosDoneLocked();
- }
- }
-
- WebViewProviderResponse waitForAndGetProvider() {
- PackageInfo webViewPackage = null;
- final long NS_PER_MS = 1000000;
- final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
- boolean webViewReady = false;
- int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
- synchronized (mLock) {
- webViewReady = webViewIsReadyLocked();
- while (!webViewReady) {
- final long timeNowMs = System.nanoTime() / NS_PER_MS;
- if (timeNowMs >= timeoutTimeMs) break;
- try {
- mLock.wait(timeoutTimeMs - timeNowMs);
- } catch (InterruptedException e) {}
- webViewReady = webViewIsReadyLocked();
- }
- // Make sure we return the provider that was used to create the relro file
- webViewPackage = mCurrentWebViewPackage;
- if (webViewReady) {
- } else if (!mAnyWebViewInstalled) {
- webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
- } else {
- // Either the current relro creation isn't done yet, or the new relro creatioin
- // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
- webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
- Slog.e(TAG, "Timed out waiting for relro creation, relros started "
- + mNumRelroCreationsStarted
- + " relros finished " + mNumRelroCreationsFinished
- + " package dirty? " + mWebViewPackageDirty);
- }
- }
- if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
- return new WebViewProviderResponse(webViewPackage, webViewStatus);
- }
-
- PackageInfo getCurrentWebViewPackage() {
- synchronized(mLock) {
- return mCurrentWebViewPackage;
- }
- }
-
- /**
- * Returns whether WebView is ready and is not going to go through its preparation phase
- * again directly.
- */
- private boolean webViewIsReadyLocked() {
- return !mWebViewPackageDirty
- && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
- // The current package might be replaced though we haven't received an intent
- // declaring this yet, the following flag makes anyone loading WebView to wait in
- // this case.
- && mAnyWebViewInstalled;
- }
-
- private void checkIfRelrosDoneLocked() {
- if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
- if (mWebViewPackageDirty) {
- mWebViewPackageDirty = false;
- // If we have changed provider since we started the relro creation we need to
- // redo the whole process using the new package instead.
- try {
- PackageInfo newPackage = findPreferredWebViewPackage();
- onWebViewProviderChanged(newPackage);
- } catch (WebViewPackageMissingException e) {
- mCurrentWebViewPackage = null;
- // If we can't find any valid WebView package we are now in a state where
- // mAnyWebViewInstalled is false, so loading WebView will be blocked and we
- // should simply wait until we receive an intent declaring a new package was
- // installed.
- }
- } else {
- mLock.notifyAll();
- }
- }
- }
-
- /**
- * Returns whether this provider is valid for use as a WebView provider.
- */
- boolean isValidProvider(WebViewProviderInfo configInfo, PackageInfo packageInfo) {
- return VALIDITY_OK == validityResult(configInfo, packageInfo);
- }
-
- private int validityResult(WebViewProviderInfo configInfo, PackageInfo packageInfo) {
- // Ensure the provider targets this framework release (or a later one).
- if (!UserPackage.hasCorrectTargetSdkVersion(packageInfo)) {
- return VALIDITY_INCORRECT_SDK_VERSION;
- }
- if (!versionCodeGE(packageInfo.getLongVersionCode(), getMinimumVersionCode())
- && !mSystemInterface.systemIsDebuggable()) {
- // Webview providers may be downgraded arbitrarily low, prevent that by enforcing
- // minimum version code. This check is only enforced for user builds.
- return VALIDITY_INCORRECT_VERSION_CODE;
- }
- if (!providerHasValidSignature(configInfo, packageInfo, mSystemInterface)) {
- return VALIDITY_INCORRECT_SIGNATURE;
- }
- if (WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) == null) {
- return VALIDITY_NO_LIBRARY_FLAG;
- }
- return VALIDITY_OK;
- }
-
- /**
- * Both versionCodes should be from a WebView provider package implemented by Chromium.
- * VersionCodes from other kinds of packages won't make any sense in this method.
- *
- * An introduction to Chromium versionCode scheme:
- * "BBBBPPPAX"
- * BBBB: 4 digit branch number. It monotonically increases over time.
- * PPP: patch number in the branch. It is padded with zeroes to the left. These three digits
- * may change their meaning in the future.
- * A: architecture digit.
- * X: A digit to differentiate APKs for other reasons.
- *
- * This method takes the "BBBB" of versionCodes and compare them.
- *
- * @return true if versionCode1 is higher than or equal to versionCode2.
- */
- private static boolean versionCodeGE(long versionCode1, long versionCode2) {
- long v1 = versionCode1 / 100000;
- long v2 = versionCode2 / 100000;
-
- return v1 >= v2;
- }
-
- /**
- * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode
- * of all available-by-default WebView provider packages. If there is no such WebView provider
- * package on the system, then return -1, which means all positive versionCode WebView packages
- * are accepted.
- *
- * Note that this is a private method in WebViewUpdater that handles a variable
- * (mMinimumVersionCode) which is shared between threads. Furthermore, this method does not
- * hold mLock meaning that we must take extra care to ensure this method is thread-safe.
- */
- private long getMinimumVersionCode() {
- if (mMinimumVersionCode > 0) {
- return mMinimumVersionCode;
- }
-
- long minimumVersionCode = -1;
- for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
- if (provider.availableByDefault) {
- try {
- long versionCode =
- mSystemInterface.getFactoryPackageVersion(provider.packageName);
- if (minimumVersionCode < 0 || versionCode < minimumVersionCode) {
- minimumVersionCode = versionCode;
- }
- } catch (NameNotFoundException e) {
- // Safe to ignore.
- }
- }
- }
-
- mMinimumVersionCode = minimumVersionCode;
- return mMinimumVersionCode;
- }
-
- private static boolean providerHasValidSignature(WebViewProviderInfo provider,
- PackageInfo packageInfo, SystemInterface systemInterface) {
- // Skip checking signatures on debuggable builds, for development purposes.
- if (systemInterface.systemIsDebuggable()) return true;
-
- // Allow system apps to be valid providers regardless of signature.
- if (packageInfo.applicationInfo.isSystemApp()) return true;
-
- // We don't support packages with multiple signatures.
- if (packageInfo.signatures.length != 1) return false;
-
- // If any of the declared signatures match the package signature, it's valid.
- for (Signature signature : provider.signatures) {
- if (signature.equals(packageInfo.signatures[0])) return true;
- }
-
- return false;
- }
-
- void dumpState(PrintWriter pw) {
- synchronized (mLock) {
- if (mCurrentWebViewPackage == null) {
- pw.println(" Current WebView package is null");
- } else {
- pw.println(String.format(" Current WebView package (name, version): (%s, %s)",
- mCurrentWebViewPackage.packageName,
- mCurrentWebViewPackage.versionName));
- }
- pw.println(String.format(" Minimum targetSdkVersion: %d",
- UserPackage.MINIMUM_SUPPORTED_SDK));
- pw.println(String.format(" Minimum WebView version code: %d",
- mMinimumVersionCode));
- pw.println(String.format(" Number of relros started: %d",
- mNumRelroCreationsStarted));
- pw.println(String.format(" Number of relros finished: %d",
- mNumRelroCreationsFinished));
- pw.println(String.format(" WebView package dirty: %b", mWebViewPackageDirty));
- pw.println(String.format(" Any WebView package installed: %b",
- mAnyWebViewInstalled));
-
- try {
- PackageInfo preferredWebViewPackage = findPreferredWebViewPackage();
- pw.println(String.format(
- " Preferred WebView package (name, version): (%s, %s)",
- preferredWebViewPackage.packageName,
- preferredWebViewPackage.versionName));
- } catch (WebViewPackageMissingException e) {
- pw.println(String.format(" Preferred WebView package: none"));
- }
-
- dumpAllPackageInformationLocked(pw);
- }
- }
-
- private void dumpAllPackageInformationLocked(PrintWriter pw) {
- WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
- pw.println(" WebView packages:");
- for (WebViewProviderInfo provider : allProviders) {
- List<UserPackage> userPackages =
- mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider);
- PackageInfo systemUserPackageInfo =
- userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo();
- if (systemUserPackageInfo == null) {
- pw.println(String.format(" %s is NOT installed.", provider.packageName));
- continue;
- }
-
- int validity = validityResult(provider, systemUserPackageInfo);
- String packageDetails = String.format(
- "versionName: %s, versionCode: %d, targetSdkVersion: %d",
- systemUserPackageInfo.versionName,
- systemUserPackageInfo.getLongVersionCode(),
- systemUserPackageInfo.applicationInfo.targetSdkVersion);
- if (validity == VALIDITY_OK) {
- boolean installedForAllUsers = isInstalledAndEnabledForAllUsers(
- mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider));
- pw.println(String.format(
- " Valid package %s (%s) is %s installed/enabled for all users",
- systemUserPackageInfo.packageName,
- packageDetails,
- installedForAllUsers ? "" : "NOT"));
- } else {
- pw.println(String.format(" Invalid package %s (%s), reason: %s",
- systemUserPackageInfo.packageName,
- packageDetails,
- getInvalidityReason(validity)));
- }
- }
- }
-
- private static String getInvalidityReason(int invalidityReason) {
- switch (invalidityReason) {
- case VALIDITY_INCORRECT_SDK_VERSION:
- return "SDK version too low";
- case VALIDITY_INCORRECT_VERSION_CODE:
- return "Version code too low";
- case VALIDITY_INCORRECT_SIGNATURE:
- return "Incorrect signature";
- case VALIDITY_NO_LIBRARY_FLAG:
- return "No WebView-library manifest flag";
- default:
- return "Unexcepted validity-reason";
- }
- }
-
-}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 4c2d0d0..9e21167 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -489,18 +489,18 @@
public void onAppWindowTransitionLocked(int displayId, int transition) {
if (DEBUG_WINDOW_TRANSITIONS) {
Slog.i(LOG_TAG, "Window transition: "
- + AppTransition.appTransitionToString(transition)
+ + AppTransition.appTransitionOldToString(transition)
+ " displayId: " + displayId);
}
final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
if (magnifying) {
switch (transition) {
- case WindowManager.TRANSIT_ACTIVITY_OPEN:
- case WindowManager.TRANSIT_TASK_OPEN:
- case WindowManager.TRANSIT_TASK_TO_FRONT:
- case WindowManager.TRANSIT_WALLPAPER_OPEN:
- case WindowManager.TRANSIT_WALLPAPER_CLOSE:
- case WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN: {
+ case WindowManager.TRANSIT_OLD_ACTIVITY_OPEN:
+ case WindowManager.TRANSIT_OLD_TASK_OPEN:
+ case WindowManager.TRANSIT_OLD_TASK_TO_FRONT:
+ case WindowManager.TRANSIT_OLD_WALLPAPER_OPEN:
+ case WindowManager.TRANSIT_OLD_WALLPAPER_CLOSE:
+ case WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN: {
mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
}
}
@@ -510,7 +510,7 @@
public void onWindowTransitionLocked(WindowState windowState, int transition) {
if (DEBUG_WINDOW_TRANSITIONS) {
Slog.i(LOG_TAG, "Window transition: "
- + AppTransition.appTransitionToString(transition)
+ + AppTransition.appTransitionOldToString(transition)
+ " displayId: " + windowState.getDisplayId());
}
final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 9d08b1b..f50ce1ab8e8 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -131,7 +131,6 @@
*/
private static final int IGNORE_CALLER = -1;
private static final int INVALID_DELAY = -1;
- private static final int INVALID_TRANSITION_TYPE = -1;
// Preallocated strings we are sending to tron, so we don't have to allocate a new one every
// time we log.
@@ -232,22 +231,19 @@
static TransitionInfo create(@NonNull ActivityRecord r,
@NonNull LaunchingState launchingState, @Nullable ActivityOptions options,
boolean processRunning, boolean processSwitch, int startResult) {
- int transitionType = INVALID_TRANSITION_TYPE;
+ if (startResult != START_SUCCESS && startResult != START_TASK_TO_FRONT) {
+ return null;
+ }
+ final int transitionType;
if (processRunning) {
- if (startResult == START_SUCCESS) {
- transitionType = TYPE_TRANSITION_WARM_LAUNCH;
- } else if (startResult == START_TASK_TO_FRONT) {
- transitionType = TYPE_TRANSITION_HOT_LAUNCH;
- }
- } else if (startResult == START_SUCCESS || startResult == START_TASK_TO_FRONT) {
+ transitionType = r.attachedToProcess()
+ ? TYPE_TRANSITION_HOT_LAUNCH
+ : TYPE_TRANSITION_WARM_LAUNCH;
+ } else {
// Task may still exist when cold launching an activity and the start result will be
// set to START_TASK_TO_FRONT. Treat this as a COLD launch.
transitionType = TYPE_TRANSITION_COLD_LAUNCH;
}
- if (transitionType == INVALID_TRANSITION_TYPE) {
- // That means the startResult is neither START_SUCCESS nor START_TASK_TO_FRONT.
- return null;
- }
return new TransitionInfo(r, launchingState, options, transitionType, processRunning,
processSwitch);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4a7fc7f0..0a7f08b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -102,10 +102,12 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
-import static android.view.WindowManager.TRANSIT_UNSET;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
+import static android.view.WindowManager.TRANSIT_OLD_UNSET;
+import static android.view.WindowManager.TransitionOldType;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -179,6 +181,7 @@
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
import static com.android.server.wm.Task.ActivityState.DESTROYED;
import static com.android.server.wm.Task.ActivityState.DESTROYING;
@@ -496,7 +499,6 @@
// process that it is hidden.
private boolean mLastDeferHidingClient; // If true we will defer setting mClientVisible to false
// and reporting to the client that it is hidden.
- private boolean mSetToSleep; // have we told the activity to sleep?
boolean nowVisible; // is this activity's window visible?
boolean mClientVisibilityDeferred;// was the visibility change message to client deferred?
boolean idle; // has the activity gone idle?
@@ -903,7 +905,6 @@
pw.print(" finishing="); pw.println(finishing);
pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused);
pw.print(" inHistory="); pw.print(inHistory);
- pw.print(" setToSleep="); pw.print(mSetToSleep);
pw.print(" idle="); pw.print(idle);
pw.print(" mStartingWindowState=");
pw.println(startingWindowStateToString(mStartingWindowState));
@@ -1313,7 +1314,7 @@
if (prevDc.mOpeningApps.remove(this)) {
// Transfer opening transition to new display.
mDisplayContent.mOpeningApps.add(this);
- mDisplayContent.prepareAppTransition(prevDc.mAppTransition.getAppTransition(), true);
+ mDisplayContent.transferAppTransitionFrom(prevDc);
mDisplayContent.executeAppTransition();
}
@@ -1361,6 +1362,7 @@
} else if (mLetterbox != null) {
mLetterbox.hide();
}
+ task.maybeUpdateLetterboxBounds(this, getLetterboxParams(w));
}
void updateLetterboxSurface(WindowState winHint) {
@@ -1374,6 +1376,12 @@
}
}
+ @Nullable
+ private Rect getLetterboxParams(WindowState w) {
+ boolean isLetterboxed = w.isLetterboxedAppWindow() && fillsParent();
+ return isLetterboxed ? getBounds() : null;
+ }
+
Rect getLetterboxInsets() {
if (mLetterbox != null) {
return mLetterbox.getInsets();
@@ -2574,7 +2582,7 @@
final boolean endTask = task.getActivityBelow(this) == null
&& !task.isClearingToReuseTask();
- final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
+ final int transit = endTask ? TRANSIT_OLD_TASK_CLOSE : TRANSIT_OLD_ACTIVITY_CLOSE;
if (isState(RESUMED)) {
if (endTask) {
mAtmService.getTaskChangeNotificationController().notifyTaskRemovalStarted(
@@ -2587,11 +2595,16 @@
if (DEBUG_VISIBILITY || DEBUG_TRANSITION) {
Slog.v(TAG_TRANSITION, "Prepare close transition: finishing " + this);
}
- mDisplayContent.prepareAppTransition(transit, false);
+ mDisplayContent.prepareAppTransitionOld(transit, false);
+ mDisplayContent.prepareAppTransition(TRANSIT_CLOSE);
// When finishing the activity preemptively take the snapshot before the app window
// is marked as hidden and any configuration changes take place
- if (mAtmService.mWindowManager.mTaskSnapshotController != null) {
+ // Note that RecentsAnimation will handle task snapshot while switching apps with
+ // the best capture timing (e.g. IME window capture),
+ // No need additional task capture while task is controlled by RecentsAnimation.
+ if (mAtmService.mWindowManager.mTaskSnapshotController != null
+ && !task.isAnimatingByRecents()) {
final ArraySet<Task> tasks = Sets.newArraySet(task);
mAtmService.mWindowManager.mTaskSnapshotController.snapshotTasks(tasks);
mAtmService.mWindowManager.mTaskSnapshotController
@@ -2658,15 +2671,16 @@
}
}
- private void prepareActivityHideTransitionAnimationIfOvarlay(int transit) {
+ private void prepareActivityHideTransitionAnimationIfOvarlay(@TransitionOldType int transit) {
if (mTaskOverlay) {
prepareActivityHideTransitionAnimation(transit);
}
}
- private void prepareActivityHideTransitionAnimation(int transit) {
+ private void prepareActivityHideTransitionAnimation(@TransitionOldType int transit) {
final DisplayContent dc = mDisplayContent;
- dc.prepareAppTransition(transit, false);
+ dc.prepareAppTransitionOld(transit, false);
+ dc.prepareAppTransition(TRANSIT_CLOSE);
setVisibility(false);
dc.executeAppTransition();
}
@@ -3583,7 +3597,7 @@
}
/**
- * @return {@code true} if the activity windowing mode is not
+ * @return {@code true} if the activity windowing mode is not in
* {@link android.app.WindowConfiguration#WINDOWING_MODE_PINNED} and a) activity
* contains windows that have {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} set or if the
* activity has set {@link #mShowWhenLocked}, or b) if the activity has set
@@ -4217,7 +4231,10 @@
// Note that we ignore display frozen since we want the opening / closing transition type
// can be updated correctly even display frozen, and it's safe since in applyAnimation will
// still check DC#okToAnimate again if the transition animation is fine to apply.
- if (okToAnimate(true /* ignoreFrozen */) && appTransition.isTransitionSet()) {
+ // TODO(new-app-transition): Rewrite this logic using WM Shell.
+ final boolean recentsAnimating = isAnimating(PARENTS, ANIMATION_TYPE_RECENTS);
+ if (okToAnimate(true /* ignoreFrozen */) && (appTransition.isTransitionSet()
+ || (recentsAnimating && !isActivityTypeHome()))) {
if (visible) {
displayContent.mOpeningApps.add(this);
mEnteringAnimation = true;
@@ -4225,7 +4242,7 @@
displayContent.mClosingApps.add(this);
mEnteringAnimation = false;
}
- if (appTransition.getAppTransition() == TRANSIT_TASK_OPEN_BEHIND) {
+ if (appTransition.getAppTransitionOld() == TRANSIT_OLD_TASK_OPEN_BEHIND) {
// We're launchingBehind, add the launching activity to mOpeningApps.
final WindowState win = getDisplayContent().findFocusedWindow();
if (win != null) {
@@ -4663,14 +4680,13 @@
return false;
}
- // Check if the activity is on a sleeping display
- // TODO b/163993448 mSetToSleep is required when restarting an existing activity, try to
- // remove it if possible.
- if (mSetToSleep && mDisplayContent.isSleeping()) {
- return false;
+ // Check if the activity is on a sleeping display, canTurnScreenOn will also check
+ // keyguard visibility
+ if (mDisplayContent.isSleeping()) {
+ return canTurnScreenOn();
+ } else {
+ return mStackSupervisor.getKeyguardController().checkKeyguardVisibility(this);
}
-
- return mStackSupervisor.getKeyguardController().checkKeyguardVisibility(this);
}
void updateVisibilityIgnoringKeyguard(boolean behindFullscreenActivity) {
@@ -4707,7 +4723,6 @@
stack.mUndrawnActivitiesBelowTopTranslucent.add(this);
}
setVisibility(true);
- mSetToSleep = false;
app.postPendingUiCleanMsg(true);
if (reportToClient) {
mClientVisibilityDeferred = false;
@@ -5106,9 +5121,6 @@
mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
StopActivityItem.obtain(configChangeFlags));
- if (stack.shouldSleepOrShutDownActivities()) {
- setSleeping(true);
- }
mAtmService.mH.postDelayed(mStopTimeoutRunnable, STOP_TIMEOUT);
} catch (Exception e) {
// Maybe just ignore exceptions here... if the process has crashed, our death
@@ -5699,10 +5711,6 @@
return mVisibleRequested || nowVisible || mState == PAUSING || mState == RESUMED;
}
- void setSleeping(boolean sleeping) {
- mSetToSleep = sleeping;
- }
-
static int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r == null || r.getParent() == null) {
@@ -6086,7 +6094,8 @@
@Override
void prepareSurfaces() {
- final boolean show = isVisible() || isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION);
+ final boolean show = isVisible() || isAnimating(PARENTS,
+ ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
if (mSurfaceControl != null) {
if (show && !mLastSurfaceShowing) {
@@ -6193,7 +6202,7 @@
super.onAnimationFinished(type, anim);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AR#onAnimationFinished");
- mTransit = TRANSIT_UNSET;
+ mTransit = TRANSIT_OLD_UNSET;
mTransitFlags = 0;
mNeedsAnimationBoundsLayer = false;
@@ -6402,6 +6411,10 @@
mLastReportedConfiguration.setConfiguration(global, override);
}
+ boolean hasCompatDisplayInsets() {
+ return mCompatDisplayInsets != null;
+ }
+
/**
* @return {@code true} if this activity is in size compatibility mode that uses the different
* density than its parent or its bounds don't fit in parent naturally.
@@ -6535,12 +6548,20 @@
mCompatDisplayInsets = new CompatDisplayInsets(mDisplayContent, this);
}
- @VisibleForTesting
- void clearSizeCompatMode() {
+ void clearSizeCompatMode(boolean recomputeTask) {
mSizeCompatScale = 1f;
mSizeCompatBounds = null;
mCompatDisplayInsets = null;
- onRequestedOverrideConfigurationChanged(EMPTY);
+
+ if (recomputeTask) {
+ // Recompute from Task because letterbox can also happen on Task level.
+ task.onRequestedOverrideConfigurationChanged(task.getRequestedOverrideConfiguration());
+ }
+ }
+
+ @VisibleForTesting
+ void clearSizeCompatMode() {
+ clearSizeCompatMode(true /* recomputeTask */);
}
@Override
@@ -6649,9 +6670,10 @@
? requestedOrientation
: newParentConfiguration.orientation;
int rotation = newParentConfiguration.windowConfiguration.getRotation();
- final boolean canChangeOrientation = handlesOrientationChangeFromDescendant();
- if (canChangeOrientation && !mCompatDisplayInsets.mIsFloating) {
- // Use parent rotation because the original display can rotate by requested orientation.
+ final boolean isFixedToUserRotation = mDisplayContent == null
+ || mDisplayContent.getDisplayRotation().isFixedToUserRotation();
+ if (!isFixedToUserRotation && !mCompatDisplayInsets.mIsFloating) {
+ // Use parent rotation because the original display can be rotated.
resolvedConfig.windowConfiguration.setRotation(rotation);
} else {
final int overrideRotation = resolvedConfig.windowConfiguration.getRotation();
@@ -6667,7 +6689,7 @@
final Rect containingAppBounds = new Rect();
final Rect containingBounds = mTmpBounds;
mCompatDisplayInsets.getContainerBounds(containingAppBounds, containingBounds, rotation,
- orientation, orientationRequested, canChangeOrientation);
+ orientation, orientationRequested, isFixedToUserRotation);
resolvedBounds.set(containingBounds);
// The size of floating task is fixed (only swap), so the aspect ratio is already correct.
if (!mCompatDisplayInsets.mIsFloating) {
@@ -7526,7 +7548,7 @@
/**
* Determines whether this ActivityRecord can turn the screen on. It checks whether the flag
* {@link ActivityRecord#getTurnScreenOnFlag} is set and checks whether the ActivityRecord
- * should be visible depending on Keyguard and window state.
+ * should be visible depending on Keyguard state.
*
* @return true if the screen can be turned on, false otherwise.
*/
@@ -7535,7 +7557,7 @@
return false;
}
final Task stack = getRootTask();
- return stack != null && !stack.inMultiWindowMode()
+ return mCurrentLaunchCanTurnScreenOn && stack != null
&& mStackSupervisor.getKeyguardController().checkKeyguardVisibility(this);
}
@@ -7736,7 +7758,7 @@
final Rect[] mStableInsets = new Rect[4];
/** Constructs the environment to simulate the bounds behavior of the given container. */
- CompatDisplayInsets(DisplayContent display, WindowContainer container) {
+ CompatDisplayInsets(DisplayContent display, ActivityRecord container) {
mIsFloating = container.getWindowConfiguration().tasksAreFloating();
if (mIsFloating) {
final Rect containerBounds = container.getWindowConfiguration().getBounds();
@@ -7752,16 +7774,23 @@
return;
}
- // If the activity is not floating, assume it fills the display.
- mWidth = display.mBaseDisplayWidth;
- mHeight = display.mBaseDisplayHeight;
+ if (container.getTask().isTaskLetterboxed()) {
+ // For apps in Task letterbox, it should fill the task bounds.
+ final Rect taskBounds = container.getTask().getBounds();
+ mWidth = taskBounds.width();
+ mHeight = taskBounds.height();
+ } else {
+ // If the activity is not floating nor letterboxed, assume it fills the display.
+ mWidth = display.mBaseDisplayWidth;
+ mHeight = display.mBaseDisplayHeight;
+ }
final DisplayPolicy policy = display.getDisplayPolicy();
for (int rotation = 0; rotation < 4; rotation++) {
mNonDecorInsets[rotation] = new Rect();
mStableInsets[rotation] = new Rect();
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
- final int dw = rotated ? mHeight : mWidth;
- final int dh = rotated ? mWidth : mHeight;
+ final int dw = rotated ? display.mBaseDisplayHeight : display.mBaseDisplayWidth;
+ final int dh = rotated ? display.mBaseDisplayWidth : display.mBaseDisplayHeight;
final DisplayCutout cutout = display.calculateDisplayCutoutForRotation(rotation)
.getDisplayCutout();
policy.getNonDecorInsetsLw(rotation, dw, dh, cutout, mNonDecorInsets[rotation]);
@@ -7787,7 +7816,7 @@
/** Gets the horizontal centered container bounds for size compatibility mode. */
void getContainerBounds(Rect outAppBounds, Rect outBounds, int rotation, int orientation,
- boolean orientationRequested, boolean canChangeOrientation) {
+ boolean orientationRequested, boolean isFixedToUserRotation) {
getFrameByOrientation(outBounds, orientation);
if (mIsFloating) {
outAppBounds.set(outBounds);
@@ -7800,7 +7829,7 @@
final boolean isOrientationMismatched =
((outBounds.width() > outBounds.height()) != (dW > dH));
- if (isOrientationMismatched && !canChangeOrientation && orientationRequested) {
+ if (isOrientationMismatched && isFixedToUserRotation && orientationRequested) {
// The orientation is mismatched but the display cannot rotate. The bounds will fit
// to the short side of container.
if (orientation == ORIENTATION_LANDSCAPE) {
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index b4fea90..a068d2b 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -820,7 +820,6 @@
}
mService.getPackageManagerInternalLocked().notifyPackageUse(
r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
- r.setSleeping(false);
r.forceNewConfig = false;
mService.getAppWarningsLocked().onStartActivity(r);
r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
@@ -2040,6 +2039,11 @@
final ActivityRecord prevTopActivity = mTopResumedActivity;
final Task topStack = mRootWindowContainer.getTopDisplayFocusedStack();
if (topStack == null || topStack.mResumedActivity == prevTopActivity) {
+ if (mService.isSleepingLocked()) {
+ // There won't be a next resumed activity. The top process should still be updated
+ // according to the current top focused activity.
+ mService.updateTopApp(null /* topResumedActivity */);
+ }
return;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c8a8f81..25b2523 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1195,7 +1195,12 @@
}
}
- mService.onStartActivitySetDidAppSwitch();
+ // Only allow app switching to be resumed if activity is not a restricted background
+ // activity, otherwise any background activity started in background task can stop
+ // home button protection mode.
+ if (!restrictedBgActivity) {
+ mService.onStartActivitySetDidAppSwitch();
+ }
mController.doPendingActivityLaunches(false);
mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
@@ -1260,6 +1265,20 @@
return false;
}
+ // Always allow home application to start activities.
+ if (mService.mHomeProcess != null && callingUid == mService.mHomeProcess.mUid) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(TAG, "Activity start allowed for home app callingUid (" + callingUid + ")");
+ }
+ return false;
+ }
+
+ // App switching will be allowed if BAL app switching flag is not enabled, or if
+ // its app switching rule allows it.
+ // This is used to block background activity launch even if the app is still
+ // visible to user after user clicking home button.
+ final boolean appSwitchAllowed = mService.getBalAppSwitchesAllowed();
+
// don't abort if the callingUid has a visible window or is a persistent system process
final int callingUidProcState = mService.getUidState(callingUid);
final boolean callingUidHasAnyVisibleWindow =
@@ -1269,7 +1288,8 @@
|| callingUidProcState == ActivityManager.PROCESS_STATE_BOUND_TOP;
final boolean isCallingUidPersistentSystemProcess =
callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
- if (callingUidHasAnyVisibleWindow || isCallingUidPersistentSystemProcess) {
+ if ((appSwitchAllowed && callingUidHasAnyVisibleWindow)
+ || isCallingUidPersistentSystemProcess) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed: callingUidHasAnyVisibleWindow = " + callingUid
+ ", isCallingUidPersistentSystemProcess = "
@@ -1295,6 +1315,7 @@
|| realCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
if (realCallingUid != callingUid) {
// don't abort if the realCallingUid has a visible window
+ // TODO(b/171459802): We should check appSwitchAllowed also
if (realCallingUidHasAnyVisibleWindow) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
@@ -1376,7 +1397,7 @@
// don't abort if the callerApp or other processes of that uid are allowed in any way
if (callerApp != null) {
// first check the original calling process
- if (callerApp.areBackgroundActivityStartsAllowed()) {
+ if (callerApp.areBackgroundActivityStartsAllowed(appSwitchAllowed)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Background activity start allowed: callerApp process (pid = "
+ callerApp.getPid() + ", uid = " + callerAppUid + ") is allowed");
@@ -1389,7 +1410,8 @@
if (uidProcesses != null) {
for (int i = uidProcesses.size() - 1; i >= 0; i--) {
final WindowProcessController proc = uidProcesses.valueAt(i);
- if (proc != callerApp && proc.areBackgroundActivityStartsAllowed()) {
+ if (proc != callerApp
+ && proc.areBackgroundActivityStartsAllowed(appSwitchAllowed)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG,
"Background activity start allowed: process " + proc.getPid()
@@ -2546,6 +2568,10 @@
private void resumeTargetStackIfNeeded() {
if (mDoResume) {
+ final ActivityRecord next = mTargetStack.topRunningActivity(true /* focusableOnly */);
+ if (next != null) {
+ next.setCurrentLaunchCanTurnScreenOn(true);
+ }
mRootWindowContainer.resumeFocusedStacksTopActivities(mTargetStack, null, mOptions);
} else {
ActivityOptions.abort(mOptions);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 25b407a..6749cdf 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -39,6 +39,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ApplicationInfo.FLAG_FACTORY_TEST;
import static android.content.pm.ConfigurationInfo.GL_ES_VERSION_UNDEFINED;
import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
@@ -65,6 +66,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
@@ -158,9 +160,11 @@
import android.app.admin.DevicePolicyCache;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
+import android.app.compat.CompatChanges;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.EnterPipRequestedItem;
import android.app.usage.UsageStatsManagerInternal;
+import android.compat.annotation.ChangeId;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -339,6 +343,12 @@
/** This activity is being relaunched due to a free-resize operation. */
public static final int RELAUNCH_REASON_FREE_RESIZE = 2;
+ /**
+ * Apps are blocked from starting activities in the foreground after the user presses home.
+ */
+ @ChangeId
+ public static final long BLOCK_ACTIVITY_STARTS_AFTER_HOME = 159433730L;
+
Context mContext;
/**
@@ -1294,6 +1304,7 @@
a.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
a.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
+ a.resizeMode = RESIZE_MODE_UNRESIZEABLE;
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchActivityType(ACTIVITY_TYPE_DREAM);
@@ -1659,11 +1670,8 @@
try {
// Collect information about the target of the Intent.
- ActivityInfo aInfo = mStackSupervisor.resolveActivity(intent, resolvedType,
- 0 /* startFlags */, null /* profilerInfo */, userId,
- ActivityStarter.computeResolveFilterUid(callingUid, callingUid,
- UserHandle.USER_NULL));
- aInfo = mAmInternal.getActivityInfoForUser(aInfo, userId);
+ final ActivityInfo aInfo = resolveActivityInfoForIntent(intent, resolvedType, userId,
+ callingUid);
synchronized (mGlobalLock) {
return mStackSupervisor.canPlaceEntityOnDisplay(displayId, callingPid, callingUid,
@@ -1674,6 +1682,15 @@
}
}
+ ActivityInfo resolveActivityInfoForIntent(Intent intent, String resolvedType,
+ int userId, int callingUid) {
+ ActivityInfo aInfo = mStackSupervisor.resolveActivity(intent, resolvedType,
+ 0 /* startFlags */, null /* profilerInfo */, userId,
+ ActivityStarter.computeResolveFilterUid(callingUid, callingUid,
+ UserHandle.USER_NULL));
+ return mAmInternal.getActivityInfoForUser(aInfo, userId);
+ }
+
/**
* This is the internal entry point for handling Activity.finish().
*
@@ -2617,8 +2634,35 @@
throw new SecurityException(msg);
}
+ /**
+ * Return true if app switch protection will be handled by background activity launch logic.
+ */
+ boolean getBalAppSwitchesProtectionEnabled() {
+ return CompatChanges.isChangeEnabled(BLOCK_ACTIVITY_STARTS_AFTER_HOME);
+ }
+
+ /**
+ * Return true if app switching is allowed.
+ */
+ boolean getBalAppSwitchesAllowed() {
+ if (getBalAppSwitchesProtectionEnabled()) {
+ // Apps no longer able to start BAL again until app switching is resumed.
+ return mAppSwitchesAllowedTime == 0;
+ } else {
+ // Legacy behavior, BAL logic won't block app switching.
+ return true;
+ }
+ }
+
boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid,
int callingPid, int callingUid, String name) {
+
+ // Background activity launch logic replaces app switching protection, so allow
+ // apps to start activity here now.
+ if (getBalAppSwitchesProtectionEnabled()) {
+ return true;
+ }
+
if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) {
return true;
}
@@ -3599,7 +3643,7 @@
}
/** This can be called with or without the global lock held. */
- private void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
+ void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
if (!getRecentTasks().isCallerRecents(Binder.getCallingUid())) {
mAmInternal.enforceCallingPermission(permission, func);
}
@@ -4638,7 +4682,10 @@
mAppSwitchesAllowedTime = SystemClock.uptimeMillis() + APP_SWITCH_DELAY_TIME;
mLastStopAppSwitchesTime = SystemClock.uptimeMillis();
mDidAppSwitch = false;
- getActivityStartController().schedulePendingActivityLaunches(APP_SWITCH_DELAY_TIME);
+ // If BAL app switching enabled, app switches are blocked not delayed.
+ if (!getBalAppSwitchesProtectionEnabled()) {
+ getActivityStartController().schedulePendingActivityLaunches(APP_SWITCH_DELAY_TIME);
+ }
}
}
@@ -5109,7 +5156,9 @@
deferResume);
}
- kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
+ if (!deferResume) {
+ kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
+ }
} finally {
continueWindowLayout();
}
@@ -5545,11 +5594,8 @@
}
void updateTopApp(ActivityRecord topResumedActivity) {
- // If system is sleeping, use the given record (it should be null) because there won't be
- // the next resumed activity. Otherwise the process of pausing activity will keep with top
- // state even the activity has paused and stopped.
- final ActivityRecord top = mSleeping || topResumedActivity != null ? topResumedActivity
- // If there is no resumed activity, it will choose the pausing activity.
+ final ActivityRecord top = topResumedActivity != null ? topResumedActivity
+ // If there is no resumed activity, it will choose the pausing or focused activity.
: mRootWindowContainer.getTopResumedActivity();
mTopApp = top != null ? top.app : null;
}
@@ -6181,10 +6227,10 @@
if (dc == null) {
return;
}
- final boolean wasTransitionSet =
- dc.mAppTransition.getAppTransition() != TRANSIT_NONE;
+ final boolean wasTransitionSet = dc.mAppTransition.isTransitionSet();
if (!wasTransitionSet) {
- dc.prepareAppTransition(TRANSIT_NONE, false /* alwaysKeepCurrent */);
+ dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, false /* alwaysKeepCurrent */);
+ dc.prepareAppTransition(TRANSIT_NONE);
}
mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 26c701b..12c2c0e 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -17,31 +17,43 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_RELAUNCH;
-import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_CHANGE_WINDOWING_MODE;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
-import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
-import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
-import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
-import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_UNSET;
-import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE;
-import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
-import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN;
-import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_RELAUNCH;
+import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_UNSET;
+import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_OPEN;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_RELAUNCH;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation;
import static com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
@@ -104,6 +116,7 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.ArraySet;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
@@ -111,6 +124,7 @@
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.RemoteAnimationAdapter;
import android.view.WindowManager.TransitionFlags;
+import android.view.WindowManager.TransitionOldType;
import android.view.WindowManager.TransitionType;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
@@ -170,9 +184,10 @@
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
- private @TransitionType int mNextAppTransition = TRANSIT_UNSET;
+ private @TransitionOldType int mNextAppTransitionOld = TRANSIT_OLD_UNSET;
private @TransitionFlags int mNextAppTransitionFlags = 0;
- private int mLastUsedAppTransition = TRANSIT_UNSET;
+ private final ArrayList<Integer> mNextAppTransitionRequests = new ArrayList<>();
+ private @TransitionOldType int mLastUsedAppTransition = TRANSIT_OLD_UNSET;
private String mLastOpeningApp;
private String mLastClosingApp;
private String mLastChangingApp;
@@ -315,21 +330,36 @@
}
boolean isTransitionSet() {
- return mNextAppTransition != TRANSIT_UNSET;
+ return mNextAppTransitionOld != TRANSIT_OLD_UNSET || !mNextAppTransitionRequests.isEmpty();
}
- boolean isTransitionEqual(@TransitionType int transit) {
- return mNextAppTransition == transit;
+ // TODO(new-app-tranistion): Remove this after migrating to new app transition system.
+ boolean isTransitionOldEqual(@TransitionOldType int transit) {
+ return mNextAppTransitionOld == transit;
}
- @TransitionType int getAppTransition() {
- return mNextAppTransition;
+ boolean isUnoccluding() {
+ return WindowManagerService.sUseNewAppTransit
+ ? mNextAppTransitionRequests.contains(TRANSIT_OLD_KEYGUARD_UNOCCLUDE)
+ : mNextAppTransitionOld == TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
+ }
+
+ @TransitionOldType
+ int getAppTransitionOld() {
+ return mNextAppTransitionOld;
}
- private void setAppTransition(int transit, int flags) {
- mNextAppTransition = transit;
+ boolean transferFrom(AppTransition other) {
+ prepareAppTransitionOld(other.getAppTransitionOld(), true /* alwaysKeepCurrent */,
+ 0 /* flags */, false /* forceOverride */);
+ mNextAppTransitionRequests.addAll(other.mNextAppTransitionRequests);
+ return prepare();
+ }
+
+ private void setAppTransitionOld(@TransitionOldType int transit, int flags) {
+ mNextAppTransitionOld = transit;
mNextAppTransitionFlags |= flags;
- setLastAppTransition(TRANSIT_UNSET, null, null, null);
+ setLastAppTransition(TRANSIT_OLD_UNSET, null, null, null);
updateBooster();
if (isTransitionSet()) {
removeAppTransitionTimeoutCallbacks();
@@ -431,8 +461,9 @@
* layout pass needs to be done
*/
int goodToGo(int transit, ActivityRecord topOpeningApp, ArraySet<ActivityRecord> openingApps) {
- mNextAppTransition = TRANSIT_UNSET;
+ mNextAppTransitionOld = TRANSIT_OLD_UNSET;
mNextAppTransitionFlags = 0;
+ mNextAppTransitionRequests.clear();
setAppTransitionState(APP_STATE_RUNNING);
final WindowContainer wc =
topOpeningApp != null ? topOpeningApp.getAnimatingContainer() : null;
@@ -463,7 +494,7 @@
}
void freeze() {
- final int transit = mNextAppTransition;
+ final int transit = mNextAppTransitionOld;
// The RemoteAnimationControl didn't register AppTransitionListener and
// only initialized the finish and timeout callback when goodToGo().
// So cancel the remote animation here to prevent the animation can't do
@@ -471,7 +502,7 @@
if (mRemoteAnimationController != null) {
mRemoteAnimationController.cancelAnimation("freeze");
}
- setAppTransition(TRANSIT_UNSET, 0 /* flags */);
+ setAppTransitionOld(TRANSIT_OLD_UNSET, 0 /* flags */);
clear();
setReady();
notifyAppTransitionCancelledLocked(transit);
@@ -493,7 +524,7 @@
private boolean needsBoosting() {
final boolean recentsAnimRunning = mService.getRecentsAnimationController() != null;
- return mNextAppTransition != TRANSIT_UNSET
+ return mNextAppTransitionOld != TRANSIT_OLD_UNSET
|| mAppTransitionState == APP_STATE_READY
|| mAppTransitionState == APP_STATE_RUNNING
|| recentsAnimRunning;
@@ -645,11 +676,13 @@
}
}
- private int updateToTranslucentAnimIfNeeded(int anim, int transit) {
- if (transit == TRANSIT_TRANSLUCENT_ACTIVITY_OPEN && anim == R.anim.activity_open_enter) {
+ private int updateToTranslucentAnimIfNeeded(int anim, @TransitionOldType int transit) {
+ if (transit == TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN
+ && anim == R.anim.activity_open_enter) {
return R.anim.activity_translucent_open_enter;
}
- if (transit == TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE && anim == R.anim.activity_close_exit) {
+ if (transit == TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE
+ && anim == R.anim.activity_close_exit) {
return R.anim.activity_translucent_close_exit;
}
return anim;
@@ -752,8 +785,8 @@
set.addAnimation(alpha);
set.setDetachWallpaper(true);
a = set;
- } else if (transit == TRANSIT_WALLPAPER_INTRA_OPEN ||
- transit == TRANSIT_WALLPAPER_INTRA_CLOSE) {
+ } else if (transit == TRANSIT_OLD_WALLPAPER_INTRA_OPEN
+ || transit == TRANSIT_OLD_WALLPAPER_INTRA_CLOSE) {
// If we are on top of the wallpaper, we need an animation that
// correctly handles the wallpaper staying static behind all of
// the animated elements. To do this, will just have the existing
@@ -770,8 +803,8 @@
// task transition duration.
final long duration;
switch (transit) {
- case TRANSIT_ACTIVITY_OPEN:
- case TRANSIT_ACTIVITY_CLOSE:
+ case TRANSIT_OLD_ACTIVITY_OPEN:
+ case TRANSIT_OLD_ACTIVITY_CLOSE:
duration = mConfigShortAnimTime;
break;
default:
@@ -950,16 +983,16 @@
} else {
final long duration;
switch (transit) {
- case TRANSIT_ACTIVITY_OPEN:
- case TRANSIT_ACTIVITY_CLOSE:
+ case TRANSIT_OLD_ACTIVITY_OPEN:
+ case TRANSIT_OLD_ACTIVITY_CLOSE:
duration = mConfigShortAnimTime;
break;
default:
duration = DEFAULT_APP_TRANSITION_DURATION;
break;
}
- if (transit == TRANSIT_WALLPAPER_INTRA_OPEN ||
- transit == TRANSIT_WALLPAPER_INTRA_CLOSE) {
+ if (transit == TRANSIT_OLD_WALLPAPER_INTRA_OPEN
+ || transit == TRANSIT_OLD_WALLPAPER_INTRA_CLOSE) {
// If we are on top of the wallpaper, we need an animation that
// correctly handles the wallpaper staying static behind all of
// the animated elements. To do this, will just have the existing
@@ -1002,8 +1035,8 @@
// task transition duration.
final int duration;
switch (transit) {
- case TRANSIT_ACTIVITY_OPEN:
- case TRANSIT_ACTIVITY_CLOSE:
+ case TRANSIT_OLD_ACTIVITY_OPEN:
+ case TRANSIT_OLD_ACTIVITY_CLOSE:
duration = mConfigShortAnimTime;
break;
default:
@@ -1306,7 +1339,7 @@
}
case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
// Previous app window during the scale up
- if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
+ if (transit == TRANSIT_OLD_WALLPAPER_INTRA_OPEN) {
// Fade out the source activity if we are animating to a wallpaper
// activity.
a = new AlphaAnimation(1, 0);
@@ -1317,7 +1350,7 @@
}
case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
// Target app window during the scale down
- if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
+ if (transit == TRANSIT_OLD_WALLPAPER_INTRA_OPEN) {
// Fade in the destination activity if we are animating from a wallpaper
// activity.
a = new AlphaAnimation(0, 1);
@@ -1452,7 +1485,7 @@
}
case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
// Exiting app while the thumbnail is scaling up should fade or stay in place
- if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
+ if (transit == TRANSIT_OLD_WALLPAPER_INTRA_OPEN) {
// Fade out while bringing up selected activity. This keeps the
// current activity from showing through a launching wallpaper
// activity.
@@ -1545,7 +1578,7 @@
&& !mNextAppTransitionOverrideRequested
&& mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE
&& mNextAppTransitionType != NEXT_TRANSIT_TYPE_CLIP_REVEAL
- && mNextAppTransition != TRANSIT_KEYGUARD_GOING_AWAY;
+ && mNextAppTransitionOld != TRANSIT_OLD_KEYGUARD_GOING_AWAY;
}
RemoteAnimationController getRemoteAnimationController() {
@@ -1585,35 +1618,35 @@
Animation a;
if (isKeyguardGoingAwayTransit(transit) && enter) {
a = loadKeyguardExitAnimation(transit);
- } else if (transit == TRANSIT_KEYGUARD_OCCLUDE) {
+ } else if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) {
a = null;
- } else if (transit == TRANSIT_KEYGUARD_UNOCCLUDE && !enter) {
+ } else if (transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE && !enter) {
a = loadAnimationRes(lp, com.android.internal.R.anim.wallpaper_open_exit);
- } else if (transit == TRANSIT_CRASHING_ACTIVITY_CLOSE) {
+ } else if (transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE) {
a = null;
- } else if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN
- || transit == TRANSIT_TASK_OPEN
- || transit == TRANSIT_TASK_TO_FRONT)) {
+ } else if (isVoiceInteraction && (transit == TRANSIT_OLD_ACTIVITY_OPEN
+ || transit == TRANSIT_OLD_TASK_OPEN
+ || transit == TRANSIT_OLD_TASK_TO_FRONT)) {
a = loadAnimationRes(lp, enter
? com.android.internal.R.anim.voice_activity_open_enter
: com.android.internal.R.anim.voice_activity_open_exit);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation voice: anim=%s transit=%s isEntrance=%b Callers=%s", a,
- appTransitionToString(transit), enter, Debug.getCallers(3));
- } else if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_CLOSE
- || transit == TRANSIT_TASK_CLOSE
- || transit == TRANSIT_TASK_TO_BACK)) {
+ appTransitionOldToString(transit), enter, Debug.getCallers(3));
+ } else if (isVoiceInteraction && (transit == TRANSIT_OLD_ACTIVITY_CLOSE
+ || transit == TRANSIT_OLD_TASK_CLOSE
+ || transit == TRANSIT_OLD_TASK_TO_BACK)) {
a = loadAnimationRes(lp, enter
? com.android.internal.R.anim.voice_activity_close_enter
: com.android.internal.R.anim.voice_activity_close_exit);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation voice: anim=%s transit=%s isEntrance=%b Callers=%s", a,
- appTransitionToString(transit), enter, Debug.getCallers(3));
- } else if (transit == TRANSIT_ACTIVITY_RELAUNCH) {
+ appTransitionOldToString(transit), enter, Debug.getCallers(3));
+ } else if (transit == TRANSIT_OLD_ACTIVITY_RELAUNCH) {
a = createRelaunchAnimation(frame, insets);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: anim=%s nextAppTransition=%d transit=%s Callers=%s", a,
- mNextAppTransition, appTransitionToString(transit),
+ mNextAppTransitionOld, appTransitionOldToString(transit),
Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
a = loadAnimationRes(mNextAppTransitionPackage, enter ?
@@ -1621,25 +1654,25 @@
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM transit=%s "
+ "isEntrance=%b Callers=%s",
- a, appTransitionToString(transit), enter, Debug.getCallers(3));
+ a, appTransitionOldToString(transit), enter, Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE) {
a = loadAnimationRes(mNextAppTransitionPackage, mNextAppTransitionInPlace);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM_IN_PLACE "
+ "transit=%s Callers=%s",
- a, appTransitionToString(transit), Debug.getCallers(3));
+ a, appTransitionOldToString(transit), Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
a = createClipRevealAnimationLocked(transit, enter, frame, displayFrame);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: anim=%s nextAppTransition=ANIM_CLIP_REVEAL "
+ "transit=%s Callers=%s",
- a, appTransitionToString(transit), Debug.getCallers(3));
+ a, appTransitionOldToString(transit), Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
a = createScaleUpAnimationLocked(transit, enter, frame);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: anim=%s nextAppTransition=ANIM_SCALE_UP transit=%s "
+ "isEntrance=%s Callers=%s",
- a, appTransitionToString(transit), enter, Debug.getCallers(3));
+ a, appTransitionOldToString(transit), enter, Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP ||
mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) {
mNextAppTransitionScaleUp =
@@ -1651,7 +1684,7 @@
+ "Callers=%s",
a, mNextAppTransitionScaleUp
? "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN",
- appTransitionToString(transit), enter, Debug.getCallers(3));
+ appTransitionOldToString(transit), enter, Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP ||
mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN) {
mNextAppTransitionScaleUp =
@@ -1665,77 +1698,77 @@
a, mNextAppTransitionScaleUp
? "ANIM_THUMBNAIL_ASPECT_SCALE_UP"
: "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN",
- appTransitionToString(transit), enter, Debug.getCallers(3));
+ appTransitionOldToString(transit), enter, Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS && enter) {
a = loadAnimationRes("android",
com.android.internal.R.anim.task_open_enter_cross_profile_apps);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS: "
+ "anim=%s transit=%s isEntrance=true Callers=%s",
- a, appTransitionToString(transit), Debug.getCallers(3));
- } else if (transit == TRANSIT_TASK_CHANGE_WINDOWING_MODE) {
+ a, appTransitionOldToString(transit), Debug.getCallers(3));
+ } else if (transit == TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE) {
// In the absence of a specific adapter, we just want to keep everything stationary.
a = new AlphaAnimation(1.f, 1.f);
a.setDuration(WindowChangeAnimationSpec.ANIMATION_DURATION);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: anim=%s transit=%s isEntrance=%b Callers=%s",
- a, appTransitionToString(transit), enter, Debug.getCallers(3));
+ a, appTransitionOldToString(transit), enter, Debug.getCallers(3));
} else {
int animAttr = 0;
switch (transit) {
- case TRANSIT_ACTIVITY_OPEN:
- case TRANSIT_TRANSLUCENT_ACTIVITY_OPEN:
+ case TRANSIT_OLD_ACTIVITY_OPEN:
+ case TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN:
animAttr = enter
? WindowAnimation_activityOpenEnterAnimation
: WindowAnimation_activityOpenExitAnimation;
break;
- case TRANSIT_ACTIVITY_CLOSE:
- case TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE:
+ case TRANSIT_OLD_ACTIVITY_CLOSE:
+ case TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE:
animAttr = enter
? WindowAnimation_activityCloseEnterAnimation
: WindowAnimation_activityCloseExitAnimation;
break;
- case TRANSIT_TASK_OPEN:
+ case TRANSIT_OLD_TASK_OPEN:
animAttr = enter
? WindowAnimation_taskOpenEnterAnimation
: WindowAnimation_taskOpenExitAnimation;
break;
- case TRANSIT_TASK_CLOSE:
+ case TRANSIT_OLD_TASK_CLOSE:
animAttr = enter
? WindowAnimation_taskCloseEnterAnimation
: WindowAnimation_taskCloseExitAnimation;
break;
- case TRANSIT_TASK_TO_FRONT:
+ case TRANSIT_OLD_TASK_TO_FRONT:
animAttr = enter
? WindowAnimation_taskToFrontEnterAnimation
: WindowAnimation_taskToFrontExitAnimation;
break;
- case TRANSIT_TASK_TO_BACK:
+ case TRANSIT_OLD_TASK_TO_BACK:
animAttr = enter
? WindowAnimation_taskToBackEnterAnimation
: WindowAnimation_taskToBackExitAnimation;
break;
- case TRANSIT_WALLPAPER_OPEN:
+ case TRANSIT_OLD_WALLPAPER_OPEN:
animAttr = enter
? WindowAnimation_wallpaperOpenEnterAnimation
: WindowAnimation_wallpaperOpenExitAnimation;
break;
- case TRANSIT_WALLPAPER_CLOSE:
+ case TRANSIT_OLD_WALLPAPER_CLOSE:
animAttr = enter
? WindowAnimation_wallpaperCloseEnterAnimation
: WindowAnimation_wallpaperCloseExitAnimation;
break;
- case TRANSIT_WALLPAPER_INTRA_OPEN:
+ case TRANSIT_OLD_WALLPAPER_INTRA_OPEN:
animAttr = enter
? WindowAnimation_wallpaperIntraOpenEnterAnimation
: WindowAnimation_wallpaperIntraOpenExitAnimation;
break;
- case TRANSIT_WALLPAPER_INTRA_CLOSE:
+ case TRANSIT_OLD_WALLPAPER_INTRA_CLOSE:
animAttr = enter
? WindowAnimation_wallpaperIntraCloseEnterAnimation
: WindowAnimation_wallpaperIntraCloseExitAnimation;
break;
- case TRANSIT_TASK_OPEN_BEHIND:
+ case TRANSIT_OLD_TASK_OPEN_BEHIND:
animAttr = enter
? WindowAnimation_launchTaskBehindSourceAnimation
: WindowAnimation_launchTaskBehindTargetAnimation;
@@ -1744,7 +1777,7 @@
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b "
+ "Callers=%s",
- a, animAttr, appTransitionToString(transit), enter,
+ a, animAttr, appTransitionOldToString(transit), enter,
Debug.getCallers(3));
}
setAppTransitionFinishedCallbackIfNeeded(a);
@@ -1760,14 +1793,16 @@
final boolean subtle =
(mNextAppTransitionFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) != 0;
return mService.mPolicy.createHiddenByKeyguardExit(
- transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER, toShade, subtle);
+ transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER, toShade, subtle);
}
int getAppStackClipMode() {
- return mNextAppTransition == TRANSIT_ACTIVITY_RELAUNCH
+ return mNextAppTransitionRequests.contains(TRANSIT_RELAUNCH)
+ || mNextAppTransitionRequests.contains(TRANSIT_KEYGUARD_GOING_AWAY)
+ || mNextAppTransitionOld == TRANSIT_OLD_ACTIVITY_RELAUNCH
|| mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL
- || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY
- || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER
+ || mNextAppTransitionOld == TRANSIT_OLD_KEYGUARD_GOING_AWAY
+ || mNextAppTransitionOld == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER
? STACK_CLIP_NONE
: STACK_CLIP_AFTER_ANIM;
}
@@ -1965,7 +2000,100 @@
@Override
public String toString() {
- return "mNextAppTransition=" + appTransitionToString(mNextAppTransition);
+ StringBuilder sb = new StringBuilder();
+ sb.append("mNextAppTransition=");
+ sb.append(appTransitionOldToString(mNextAppTransitionOld));
+ sb.append(", mNextAppTransitionRequests=[");
+
+ boolean separator = false;
+ for (Integer transit : mNextAppTransitionRequests) {
+ if (separator) {
+ sb.append(", ");
+ }
+ sb.append(appTransitionToString(transit));
+ separator = true;
+ }
+ sb.append("]");
+ sb.append(", mNextAppTransitionFlags="
+ + appTransitionFlagsToString(mNextAppTransitionFlags));
+ return sb.toString();
+ }
+
+ /**
+ * Returns the human readable name of a old window transition.
+ *
+ * @param transition The old window transition.
+ * @return The transition symbolic name.
+ */
+ public static String appTransitionOldToString(@TransitionOldType int transition) {
+ switch (transition) {
+ case TRANSIT_OLD_UNSET: {
+ return "TRANSIT_OLD_UNSET";
+ }
+ case TRANSIT_OLD_NONE: {
+ return "TRANSIT_OLD_NONE";
+ }
+ case TRANSIT_OLD_ACTIVITY_OPEN: {
+ return "TRANSIT_OLD_ACTIVITY_OPEN";
+ }
+ case TRANSIT_OLD_ACTIVITY_CLOSE: {
+ return "TRANSIT_OLD_ACTIVITY_CLOSE";
+ }
+ case TRANSIT_OLD_TASK_OPEN: {
+ return "TRANSIT_OLD_TASK_OPEN";
+ }
+ case TRANSIT_OLD_TASK_CLOSE: {
+ return "TRANSIT_OLD_TASK_CLOSE";
+ }
+ case TRANSIT_OLD_TASK_TO_FRONT: {
+ return "TRANSIT_OLD_TASK_TO_FRONT";
+ }
+ case TRANSIT_OLD_TASK_TO_BACK: {
+ return "TRANSIT_OLD_TASK_TO_BACK";
+ }
+ case TRANSIT_OLD_WALLPAPER_CLOSE: {
+ return "TRANSIT_OLD_WALLPAPER_CLOSE";
+ }
+ case TRANSIT_OLD_WALLPAPER_OPEN: {
+ return "TRANSIT_OLD_WALLPAPER_OPEN";
+ }
+ case TRANSIT_OLD_WALLPAPER_INTRA_OPEN: {
+ return "TRANSIT_OLD_WALLPAPER_INTRA_OPEN";
+ }
+ case TRANSIT_OLD_WALLPAPER_INTRA_CLOSE: {
+ return "TRANSIT_OLD_WALLPAPER_INTRA_CLOSE";
+ }
+ case TRANSIT_OLD_TASK_OPEN_BEHIND: {
+ return "TRANSIT_OLD_TASK_OPEN_BEHIND";
+ }
+ case TRANSIT_OLD_ACTIVITY_RELAUNCH: {
+ return "TRANSIT_OLD_ACTIVITY_RELAUNCH";
+ }
+ case TRANSIT_OLD_KEYGUARD_GOING_AWAY: {
+ return "TRANSIT_OLD_KEYGUARD_GOING_AWAY";
+ }
+ case TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER: {
+ return "TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER";
+ }
+ case TRANSIT_OLD_KEYGUARD_OCCLUDE: {
+ return "TRANSIT_OLD_KEYGUARD_OCCLUDE";
+ }
+ case TRANSIT_OLD_KEYGUARD_UNOCCLUDE: {
+ return "TRANSIT_OLD_KEYGUARD_UNOCCLUDE";
+ }
+ case TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN: {
+ return "TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN";
+ }
+ case TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE: {
+ return "TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE";
+ }
+ case TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE: {
+ return "TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE";
+ }
+ default: {
+ return "<UNKNOWN: " + transition + ">";
+ }
+ }
}
/**
@@ -1974,71 +2102,38 @@
* @param transition The window transition.
* @return The transition symbolic name.
*/
- public static String appTransitionToString(int transition) {
+ public static String appTransitionToString(@TransitionType int transition) {
switch (transition) {
- case TRANSIT_UNSET: {
- return "TRANSIT_UNSET";
- }
case TRANSIT_NONE: {
return "TRANSIT_NONE";
}
- case TRANSIT_ACTIVITY_OPEN: {
- return "TRANSIT_ACTIVITY_OPEN";
+ case TRANSIT_OPEN: {
+ return "TRANSIT_OPEN";
}
- case TRANSIT_ACTIVITY_CLOSE: {
- return "TRANSIT_ACTIVITY_CLOSE";
+ case TRANSIT_CLOSE: {
+ return "TRANSIT_CLOSE";
}
- case TRANSIT_TASK_OPEN: {
- return "TRANSIT_TASK_OPEN";
+ case TRANSIT_TO_FRONT: {
+ return "TRANSIT_TO_FRONT";
}
- case TRANSIT_TASK_CLOSE: {
- return "TRANSIT_TASK_CLOSE";
+ case TRANSIT_TO_BACK: {
+ return "TRANSIT_TO_BACK";
}
- case TRANSIT_TASK_TO_FRONT: {
- return "TRANSIT_TASK_TO_FRONT";
+ case TRANSIT_RELAUNCH: {
+ return "TRANSIT_RELAUNCH";
}
- case TRANSIT_TASK_TO_BACK: {
- return "TRANSIT_TASK_TO_BACK";
- }
- case TRANSIT_WALLPAPER_CLOSE: {
- return "TRANSIT_WALLPAPER_CLOSE";
- }
- case TRANSIT_WALLPAPER_OPEN: {
- return "TRANSIT_WALLPAPER_OPEN";
- }
- case TRANSIT_WALLPAPER_INTRA_OPEN: {
- return "TRANSIT_WALLPAPER_INTRA_OPEN";
- }
- case TRANSIT_WALLPAPER_INTRA_CLOSE: {
- return "TRANSIT_WALLPAPER_INTRA_CLOSE";
- }
- case TRANSIT_TASK_OPEN_BEHIND: {
- return "TRANSIT_TASK_OPEN_BEHIND";
- }
- case TRANSIT_ACTIVITY_RELAUNCH: {
- return "TRANSIT_ACTIVITY_RELAUNCH";
+ case TRANSIT_CHANGE_WINDOWING_MODE: {
+ return "TRANSIT_CHANGE_WINDOWING_MODE";
}
case TRANSIT_KEYGUARD_GOING_AWAY: {
return "TRANSIT_KEYGUARD_GOING_AWAY";
}
- case TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER: {
- return "TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER";
- }
case TRANSIT_KEYGUARD_OCCLUDE: {
return "TRANSIT_KEYGUARD_OCCLUDE";
}
case TRANSIT_KEYGUARD_UNOCCLUDE: {
return "TRANSIT_KEYGUARD_UNOCCLUDE";
}
- case TRANSIT_TRANSLUCENT_ACTIVITY_OPEN: {
- return "TRANSIT_TRANSLUCENT_ACTIVITY_OPEN";
- }
- case TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE: {
- return "TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE";
- }
- case TRANSIT_CRASHING_ACTIVITY_CLOSE: {
- return "TRANSIT_CRASHING_ACTIVITY_CLOSE";
- }
default: {
return "<UNKNOWN: " + transition + ">";
}
@@ -2085,6 +2180,41 @@
}
}
+ private static final ArrayList<Pair<Integer, String>> sFlagToString;
+
+ static {
+ sFlagToString = new ArrayList<>();
+ sFlagToString.add(new Pair<>(TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE,
+ "TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE"));
+ sFlagToString.add(new Pair<>(TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION,
+ "TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION"));
+ sFlagToString.add(new Pair<>(TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER,
+ "TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER"));
+ sFlagToString.add(new Pair<>(TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION,
+ "TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION"));
+ sFlagToString.add(new Pair<>(TRANSIT_FLAG_APP_CRASHED,
+ "TRANSIT_FLAG_APP_CRASHED"));
+ }
+
+ /**
+ * Returns the human readable names of transit flags.
+ *
+ * @param flags a bitmask combination of transit flags.
+ * @return The combination of symbolic names.
+ */
+ public static String appTransitionFlagsToString(int flags) {
+ String sep = "";
+ StringBuilder sb = new StringBuilder();
+ for (Pair<Integer, String> pair : sFlagToString) {
+ if ((flags & pair.first) != 0) {
+ sb.append(sep);
+ sb.append(pair.second);
+ sep = " | ";
+ }
+ }
+ return sb.toString();
+ }
+
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(APP_TRANSITION_STATE, mAppTransitionState);
@@ -2145,9 +2275,9 @@
pw.print(prefix); pw.print("mNextAppTransitionCallback=");
pw.println(mNextAppTransitionCallback);
}
- if (mLastUsedAppTransition != TRANSIT_NONE) {
+ if (mLastUsedAppTransition != TRANSIT_OLD_NONE) {
pw.print(prefix); pw.print("mLastUsedAppTransition=");
- pw.println(appTransitionToString(mLastUsedAppTransition));
+ pw.println(appTransitionOldToString(mLastUsedAppTransition));
pw.print(prefix); pw.print("mLastOpeningApp=");
pw.println(mLastOpeningApp);
pw.print(prefix); pw.print("mLastClosingApp=");
@@ -2165,7 +2295,7 @@
* @return true if transition is not running and should not be skipped, false if transition is
* already running
*/
- boolean prepareAppTransitionLocked(@TransitionType int transit, boolean alwaysKeepCurrent,
+ boolean prepareAppTransitionOld(@TransitionOldType int transit, boolean alwaysKeepCurrent,
@TransitionFlags int flags, boolean forceOverride) {
if (mService.mAtmService.getTransitionController().adaptLegacyPrepare(
transit, flags, forceOverride)) {
@@ -2174,78 +2304,88 @@
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Prepare app transition: transit=%s %s alwaysKeepCurrent=%b displayId=%d "
+ "Callers=%s",
- appTransitionToString(transit), this, alwaysKeepCurrent,
+ appTransitionOldToString(transit), this, alwaysKeepCurrent,
mDisplayContent.getDisplayId(), Debug.getCallers(5));
- final boolean allowSetCrashing = !isKeyguardTransit(mNextAppTransition)
- && transit == TRANSIT_CRASHING_ACTIVITY_CLOSE;
+ final boolean allowSetCrashing = !isKeyguardTransit(mNextAppTransitionOld)
+ && transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
if (forceOverride || isKeyguardTransit(transit) || !isTransitionSet()
- || mNextAppTransition == TRANSIT_NONE || allowSetCrashing) {
- setAppTransition(transit, flags);
+ || mNextAppTransitionOld == TRANSIT_OLD_NONE || allowSetCrashing) {
+ setAppTransitionOld(transit, flags);
}
// We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic
// relies on the fact that we always execute a Keyguard transition after preparing one. We
// also don't want to change away from a crashing transition.
- else if (!alwaysKeepCurrent && !isKeyguardTransit(mNextAppTransition)
- && mNextAppTransition != TRANSIT_CRASHING_ACTIVITY_CLOSE) {
- if (transit == TRANSIT_TASK_OPEN && isTransitionEqual(TRANSIT_TASK_CLOSE)) {
+ else if (!alwaysKeepCurrent && !isKeyguardTransit(mNextAppTransitionOld)
+ && mNextAppTransitionOld != TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE) {
+ if (transit == TRANSIT_OLD_TASK_OPEN && isTransitionOldEqual(TRANSIT_OLD_TASK_CLOSE)) {
// Opening a new task always supersedes a close for the anim.
- setAppTransition(transit, flags);
- } else if (transit == TRANSIT_ACTIVITY_OPEN
- && isTransitionEqual(TRANSIT_ACTIVITY_CLOSE)) {
+ setAppTransitionOld(transit, flags);
+ } else if (transit == TRANSIT_OLD_ACTIVITY_OPEN
+ && isTransitionOldEqual(TRANSIT_OLD_ACTIVITY_CLOSE)) {
// Opening a new activity always supersedes a close for the anim.
- setAppTransition(transit, flags);
- } else if (isTaskTransit(transit) && isActivityTransit(mNextAppTransition)) {
+ setAppTransitionOld(transit, flags);
+ } else if (isTaskTransit(transit) && isActivityTransit(mNextAppTransitionOld)) {
// Task animations always supersede activity animations, because if we have both, it
// usually means that activity transition were just trampoline activities.
- setAppTransition(transit, flags);
+ setAppTransitionOld(transit, flags);
}
}
return prepare();
}
+ boolean prepareAppTransition(@TransitionType int transit, @TransitionFlags int flags) {
+ mNextAppTransitionRequests.add(transit);
+ mNextAppTransitionFlags |= flags;
+ updateBooster();
+ removeAppTransitionTimeoutCallbacks();
+ mHandler.postDelayed(mHandleAppTransitionTimeoutRunnable,
+ APP_TRANSITION_TIMEOUT_MS);
+ return prepare();
+ }
+
/**
* @return true if {@param transit} is representing a transition in which Keyguard is going
* away, false otherwise
*/
public static boolean isKeyguardGoingAwayTransit(int transit) {
- return transit == TRANSIT_KEYGUARD_GOING_AWAY
- || transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+ return transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
+ || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
}
- static boolean isKeyguardTransit(int transit) {
- return isKeyguardGoingAwayTransit(transit) || transit == TRANSIT_KEYGUARD_OCCLUDE
- || transit == TRANSIT_KEYGUARD_UNOCCLUDE;
+ static boolean isKeyguardTransit(@TransitionOldType int transit) {
+ return isKeyguardGoingAwayTransit(transit) || transit == TRANSIT_OLD_KEYGUARD_OCCLUDE
+ || transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
}
- static boolean isTaskTransit(int transit) {
+ static boolean isTaskTransit(@TransitionOldType int transit) {
return isTaskOpenTransit(transit)
- || transit == TRANSIT_TASK_CLOSE
- || transit == TRANSIT_TASK_TO_BACK;
+ || transit == TRANSIT_OLD_TASK_CLOSE
+ || transit == TRANSIT_OLD_TASK_TO_BACK;
}
- private static boolean isTaskOpenTransit(int transit) {
- return transit == TRANSIT_TASK_OPEN
- || transit == TRANSIT_TASK_OPEN_BEHIND
- || transit == TRANSIT_TASK_TO_FRONT;
+ private static boolean isTaskOpenTransit(@TransitionOldType int transit) {
+ return transit == TRANSIT_OLD_TASK_OPEN
+ || transit == TRANSIT_OLD_TASK_OPEN_BEHIND
+ || transit == TRANSIT_OLD_TASK_TO_FRONT;
}
- static boolean isActivityTransit(int transit) {
- return transit == TRANSIT_ACTIVITY_OPEN
- || transit == TRANSIT_ACTIVITY_CLOSE
- || transit == TRANSIT_ACTIVITY_RELAUNCH;
+ static boolean isActivityTransit(@TransitionOldType int transit) {
+ return transit == TRANSIT_OLD_ACTIVITY_OPEN
+ || transit == TRANSIT_OLD_ACTIVITY_CLOSE
+ || transit == TRANSIT_OLD_ACTIVITY_RELAUNCH;
}
- static boolean isChangeTransit(int transit) {
- return transit == TRANSIT_TASK_CHANGE_WINDOWING_MODE;
+ static boolean isChangeTransit(@TransitionOldType int transit) {
+ return transit == TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
}
- static boolean isClosingTransit(int transit) {
- return transit == TRANSIT_ACTIVITY_CLOSE
- || transit == TRANSIT_TASK_CLOSE
- || transit == TRANSIT_WALLPAPER_CLOSE
- || transit == TRANSIT_WALLPAPER_INTRA_CLOSE
- || transit == TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE
- || transit == TRANSIT_CRASHING_ACTIVITY_CLOSE;
+ static boolean isClosingTransit(@TransitionOldType int transit) {
+ return transit == TRANSIT_OLD_ACTIVITY_CLOSE
+ || transit == TRANSIT_OLD_TASK_CLOSE
+ || transit == TRANSIT_OLD_WALLPAPER_CLOSE
+ || transit == TRANSIT_OLD_WALLPAPER_INTRA_CLOSE
+ || transit == TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE
+ || transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
}
/**
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 14eedf7..97912c1 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -17,27 +17,27 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_RELAUNCH;
-import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN;
-import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
-import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
-import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE;
-import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
-import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN;
-import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_RELAUNCH;
+import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_OPEN;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
@@ -63,7 +63,7 @@
import android.view.RemoteAnimationDefinition;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
-import android.view.WindowManager.TransitionType;
+import android.view.WindowManager.TransitionOldType;
import android.view.animation.Animation;
import com.android.internal.annotations.VisibleForTesting;
@@ -109,10 +109,12 @@
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO");
+ // TODO(new-app-transition): Compute the best transition from
+ // appTransition.mNextAppTransitionRequests
final AppTransition appTransition = mDisplayContent.mAppTransition;
- int transit = appTransition.getAppTransition();
+ int transit = appTransition.getAppTransitionOld();
if (mDisplayContent.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {
- transit = WindowManager.TRANSIT_UNSET;
+ transit = WindowManager.TRANSIT_OLD_UNSET;
}
mDisplayContent.mSkipAppTransitionAnimation = false;
mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear();
@@ -226,7 +228,7 @@
}
RemoteAnimationAdapter getRemoteAnimationOverride(@NonNull WindowContainer container,
- @TransitionType int transit, ArraySet<Integer> activityTypes) {
+ @TransitionOldType int transit, ArraySet<Integer> activityTypes) {
final RemoteAnimationDefinition definition = container.getRemoteAnimationDefinition();
if (definition != null) {
final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes);
@@ -245,8 +247,8 @@
* set of defined remote animations in the app window token.
*/
private void overrideWithRemoteAnimationIfSet(ActivityRecord animLpActivity,
- @TransitionType int transit, ArraySet<Integer> activityTypes) {
- if (transit == TRANSIT_CRASHING_ACTIVITY_CLOSE) {
+ @TransitionOldType int transit, ArraySet<Integer> activityTypes) {
+ if (transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE) {
// The crash transition has higher priority than any involved remote animations.
return;
}
@@ -269,7 +271,7 @@
/**
* @return The window token that determines the animation theme.
*/
- private ActivityRecord findAnimLayoutParamsToken(@TransitionType int transit,
+ private ActivityRecord findAnimLayoutParamsToken(@TransitionOldType int transit,
ArraySet<Integer> activityTypes) {
ActivityRecord result;
final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
@@ -357,7 +359,7 @@
* interaction session driving task.
*/
private void applyAnimations(ArraySet<WindowContainer> wcs, ArraySet<ActivityRecord> apps,
- @TransitionType int transit, boolean visible, LayoutParams animLp,
+ @TransitionOldType int transit, boolean visible, LayoutParams animLp,
boolean voiceInteraction) {
final int wcsCount = wcs.size();
for (int i = 0; i < wcsCount; i++) {
@@ -490,9 +492,9 @@
* interaction session driving task.
*/
private void applyAnimations(ArraySet<ActivityRecord> openingApps,
- ArraySet<ActivityRecord> closingApps, @TransitionType int transit,
+ ArraySet<ActivityRecord> closingApps, @TransitionOldType int transit,
LayoutParams animLp, boolean voiceInteraction) {
- if (transit == WindowManager.TRANSIT_UNSET
+ if (transit == WindowManager.TRANSIT_OLD_UNSET
|| (openingApps.isEmpty() && closingApps.isEmpty())) {
return;
}
@@ -583,7 +585,7 @@
}
}
- private void handleChangingApps(@TransitionType int transit) {
+ private void handleChangingApps(@TransitionOldType int transit) {
final ArraySet<WindowContainer> apps = mDisplayContent.mChangingContainers;
final int appsCount = apps.size();
for (int i = 0; i < appsCount; i++) {
@@ -593,8 +595,8 @@
}
}
- private void handleNonAppWindowsInTransition(@TransitionType int transit, int flags) {
- if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
+ private void handleNonAppWindowsInTransition(@TransitionOldType int transit, int flags) {
+ if (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY) {
if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
&& (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0
&& (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) == 0) {
@@ -606,10 +608,10 @@
}
}
}
- if (transit == TRANSIT_KEYGUARD_GOING_AWAY
- || transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER) {
+ if (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
+ || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER) {
mDisplayContent.startKeyguardExitOnNonAppWindows(
- transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+ transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
(flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0,
(flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) != 0);
}
@@ -689,13 +691,13 @@
return true;
}
- private int maybeUpdateTransitToWallpaper(@TransitionType int transit,
+ private int maybeUpdateTransitToWallpaper(@TransitionOldType int transit,
boolean openingAppHasWallpaper, boolean closingAppHasWallpaper) {
// Given no app transition pass it through instead of a wallpaper transition.
// Never convert the crashing transition.
// Never convert a change transition since the top activity isn't changing and will likely
// still be above an opening wallpaper.
- if (transit == TRANSIT_NONE || transit == TRANSIT_CRASHING_ACTIVITY_CLOSE
+ if (transit == TRANSIT_OLD_NONE || transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE
|| AppTransition.isChangeTransit(transit)) {
return transit;
}
@@ -705,7 +707,7 @@
&& ((wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
// Update task open transition to wallpaper transition when wallpaper is visible.
// (i.e.launching app info activity from recent tasks)
- || ((transit == TRANSIT_TASK_OPEN || transit == TRANSIT_TASK_TO_FRONT)
+ || ((transit == TRANSIT_OLD_TASK_OPEN || transit == TRANSIT_OLD_TASK_TO_FRONT)
&& mWallpaperControllerLocked.isWallpaperVisible()));
// If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set,
// don't consider upgrading to wallpaper transition.
@@ -725,10 +727,10 @@
"New wallpaper target=%s, oldWallpaper=%s, openingApps=%s, closingApps=%s",
wallpaperTarget, oldWallpaper, openingApps, closingApps);
- if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) {
- transit = TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+ if (openingCanBeWallpaperTarget && transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY) {
+ transit = TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "New transit: %s", AppTransition.appTransitionToString(transit));
+ "New transit: %s", AppTransition.appTransitionOldToString(transit));
}
// We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic
// relies on the fact that we always execute a Keyguard transition after preparing one.
@@ -736,37 +738,37 @@
if (closingAppHasWallpaper && openingAppHasWallpaper) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Wallpaper animation!");
switch (transit) {
- case TRANSIT_ACTIVITY_OPEN:
- case TRANSIT_TASK_OPEN:
- case TRANSIT_TASK_TO_FRONT:
- transit = TRANSIT_WALLPAPER_INTRA_OPEN;
+ case TRANSIT_OLD_ACTIVITY_OPEN:
+ case TRANSIT_OLD_TASK_OPEN:
+ case TRANSIT_OLD_TASK_TO_FRONT:
+ transit = TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
break;
- case TRANSIT_ACTIVITY_CLOSE:
- case TRANSIT_TASK_CLOSE:
- case TRANSIT_TASK_TO_BACK:
- transit = TRANSIT_WALLPAPER_INTRA_CLOSE;
+ case TRANSIT_OLD_ACTIVITY_CLOSE:
+ case TRANSIT_OLD_TASK_CLOSE:
+ case TRANSIT_OLD_TASK_TO_BACK:
+ transit = TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
break;
}
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "New transit: %s", AppTransition.appTransitionToString(transit));
+ "New transit: %s", AppTransition.appTransitionOldToString(transit));
} else if (oldWallpaper != null && !mDisplayContent.mOpeningApps.isEmpty()
&& !openingApps.contains(oldWallpaper.mActivityRecord)
&& closingApps.contains(oldWallpaper.mActivityRecord)
&& topClosingApp == oldWallpaper.mActivityRecord) {
// We are transitioning from an activity with a wallpaper to one without.
- transit = TRANSIT_WALLPAPER_CLOSE;
+ transit = TRANSIT_OLD_WALLPAPER_CLOSE;
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"New transit away from wallpaper: %s",
- AppTransition.appTransitionToString(transit));
+ AppTransition.appTransitionOldToString(transit));
} else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw()
&& openingApps.contains(wallpaperTarget.mActivityRecord)
&& topOpeningApp == wallpaperTarget.mActivityRecord
- && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE) {
+ && transit != TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE) {
// We are transitioning from an activity without
// a wallpaper to now showing the wallpaper
- transit = TRANSIT_WALLPAPER_OPEN;
+ transit = TRANSIT_OLD_WALLPAPER_OPEN;
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "New transit into wallpaper: %s",
- AppTransition.appTransitionToString(transit));
+ AppTransition.appTransitionOldToString(transit));
}
}
return transit;
@@ -780,12 +782,12 @@
*
* @param transit The current transition type.
* @return The current transition type or
- * {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE}/
- * {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_OPEN} if appropriate for the
+ * {@link WindowManager#TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE}/
+ * {@link WindowManager#TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN} if appropriate for the
* situation.
*/
@VisibleForTesting
- int maybeUpdateTransitToTranslucentAnim(@TransitionType int transit) {
+ int maybeUpdateTransitToTranslucentAnim(@TransitionOldType int transit) {
if (AppTransition.isChangeTransit(transit)) {
// There's no special animation to handle change animations with translucent apps
return transit;
@@ -812,10 +814,10 @@
}
if (taskOrActivity && allTranslucentClosingApps && allOpeningVisible) {
- return TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
+ return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
}
if (taskOrActivity && allTranslucentOpeningApps && mDisplayContent.mClosingApps.isEmpty()) {
- return TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
+ return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN;
}
return transit;
}
@@ -825,16 +827,16 @@
* to determine whether animations should be clipped to the task bounds instead of stack bounds.
*/
@VisibleForTesting
- boolean isTransitWithinTask(@TransitionType int transit, Task task) {
+ boolean isTransitWithinTask(@TransitionOldType int transit, Task task) {
if (task == null
|| !mDisplayContent.mChangingContainers.isEmpty()) {
// if there is no task, then we can't constrain to the task.
// if anything is changing, it can animate outside its task.
return false;
}
- if (!(transit == TRANSIT_ACTIVITY_OPEN
- || transit == TRANSIT_ACTIVITY_CLOSE
- || transit == TRANSIT_ACTIVITY_RELAUNCH)) {
+ if (!(transit == TRANSIT_OLD_ACTIVITY_OPEN
+ || transit == TRANSIT_OLD_ACTIVITY_CLOSE
+ || transit == TRANSIT_OLD_ACTIVITY_RELAUNCH)) {
// only activity-level transitions will be within-task.
return false;
}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 38ad070..5e1a26b 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -19,7 +19,6 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOW_TOKENS;
@@ -145,6 +144,11 @@
return super.getOrientation(candidate);
}
+ @Override
+ boolean handlesOrientationChangeFromDescendant() {
+ return !mIgnoreOrientationRequest && super.handlesOrientationChangeFromDescendant();
+ }
+
/**
* Sets whether this {@link DisplayArea} should ignore fixed-orientation request from apps and
* windows below it.
@@ -467,8 +471,7 @@
// Consider unoccluding only when all unknown visibilities have been
// resolved, as otherwise we just may be starting another occluding activity.
final boolean isUnoccluding =
- mDisplayContent.mAppTransition.getAppTransition()
- == TRANSIT_KEYGUARD_UNOCCLUDE
+ mDisplayContent.mAppTransition.isUnoccluding()
&& mDisplayContent.mUnknownAppVisibilityController.allResolved();
// If keyguard is showing, or we're unoccluding, force the keyguard's orientation,
// even if SystemUI hasn't updated the attrs yet.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e119929..7641de5 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -70,9 +70,9 @@
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN;
-import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT;
import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
@@ -109,7 +109,6 @@
import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
-import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE;
import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -1304,13 +1303,13 @@
@Override
boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
- ConfigurationContainer requestingContainer) {
+ WindowContainer requestingContainer) {
final Configuration config = updateOrientation(
getRequestedOverrideConfiguration(), freezeDisplayToken, false /* forceUpdate */);
// If display rotation class tells us that it doesn't consider app requested orientation,
// this display won't rotate just because of an app changes its requested orientation. Thus
// it indicates that this display chooses not to handle this request.
- final boolean handled = getDisplayRotation().respectAppRequestedOrientation();
+ final boolean handled = handlesOrientationChangeFromDescendant();
if (config == null) {
return handled;
}
@@ -1334,7 +1333,7 @@
@Override
boolean handlesOrientationChangeFromDescendant() {
- return getDisplayRotation().respectAppRequestedOrientation();
+ return !mIgnoreOrientationRequest && !getDisplayRotation().isFixedToUserRotation();
}
/**
@@ -2346,7 +2345,7 @@
@Override
int getOrientation() {
mLastOrientationSource = null;
- if (mIgnoreOrientationRequest) {
+ if (!handlesOrientationChangeFromDescendant()) {
// Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Display id=%d is ignoring all orientation requests, return %d",
@@ -4499,16 +4498,43 @@
mPointerEventDispatcher.unregisterInputEventListener(listener);
}
- void prepareAppTransition(@WindowManager.TransitionType int transit,
+ /**
+ * Transfer app transition from other display to this display.
+ *
+ * @param from Display from where the app transition is transferred.
+ *
+ * TODO(new-app-transition): Remove this once the shell handles app transition.
+ */
+ void transferAppTransitionFrom(DisplayContent from) {
+ final boolean prepared = mAppTransition.transferFrom(from.mAppTransition);
+ if (prepared && okToAnimate()) {
+ mSkipAppTransitionAnimation = false;
+ }
+ }
+
+ void prepareAppTransitionOld(@WindowManager.TransitionOldType int transit,
boolean alwaysKeepCurrent) {
- prepareAppTransition(transit, alwaysKeepCurrent, 0 /* flags */, false /* forceOverride */);
+ prepareAppTransitionOld(transit, alwaysKeepCurrent, 0 /* flags */,
+ false /* forceOverride */);
+ }
+
+ void prepareAppTransitionOld(@WindowManager.TransitionOldType int transit,
+ boolean alwaysKeepCurrent, @WindowManager.TransitionFlags int flags,
+ boolean forceOverride) {
+ final boolean prepared = mAppTransition.prepareAppTransitionOld(
+ transit, alwaysKeepCurrent, flags, forceOverride);
+ if (prepared && okToAnimate()) {
+ mSkipAppTransitionAnimation = false;
+ }
+ }
+
+ void prepareAppTransition(@WindowManager.TransitionType int transit) {
+ prepareAppTransition(transit, 0 /* flags */);
}
void prepareAppTransition(@WindowManager.TransitionType int transit,
- boolean alwaysKeepCurrent, @WindowManager.TransitionFlags int flags,
- boolean forceOverride) {
- final boolean prepared = mAppTransition.prepareAppTransitionLocked(
- transit, alwaysKeepCurrent, flags, forceOverride);
+ @WindowManager.TransitionFlags int flags) {
+ final boolean prepared = mAppTransition.prepareAppTransition(transit, flags);
if (prepared && okToAnimate()) {
mSkipAppTransitionAnimation = false;
}
@@ -4558,10 +4584,10 @@
/** Check if pending app transition is for activity / task launch. */
boolean isNextTransitionForward() {
- final int transit = mAppTransition.getAppTransition();
- return transit == TRANSIT_ACTIVITY_OPEN
- || transit == TRANSIT_TASK_OPEN
- || transit == TRANSIT_TASK_TO_FRONT;
+ final int transit = mAppTransition.getAppTransitionOld();
+ return transit == TRANSIT_OLD_ACTIVITY_OPEN
+ || transit == TRANSIT_OLD_TASK_OPEN
+ || transit == TRANSIT_OLD_TASK_TO_FRONT;
}
/**
@@ -4992,6 +5018,13 @@
|| windowingMode == WINDOWING_MODE_MULTI_WINDOW);
}
+ static boolean canReuseExistingTask(int windowingMode, int activityType) {
+ // Existing Tasks can be reused if a new stack will be created anyway, or for the Dream -
+ // because there can only ever be one DreamActivity.
+ return alwaysCreateStack(windowingMode, activityType)
+ || activityType == ACTIVITY_TYPE_DREAM;
+ }
+
@Nullable
Task getFocusedStack() {
return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedStack);
@@ -5072,7 +5105,9 @@
}
}
- kept = mAtmService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
+ if (!deferResume) {
+ kept = mAtmService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
+ }
} finally {
mAtmService.continueWindowLayout();
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index b0ddb61..baa26e6 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -66,6 +66,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
@@ -989,6 +990,11 @@
android.Manifest.permission.INTERNAL_SYSTEM_WINDOW, callingPid, callingUid,
"DisplayPolicy");
}
+ if ((attrs.privateFlags & PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP) != 0) {
+ mContext.enforcePermission(
+ android.Manifest.permission.MANAGE_ACTIVITY_STACKS, callingPid, callingUid,
+ "DisplayPolicy");
+ }
switch (attrs.type) {
case TYPE_STATUS_BAR:
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index c503431..c4aaf7c 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -857,15 +857,6 @@
return mFixedToUserRotation;
}
- /**
- * Returns {@code true} if this display rotation takes app requested orientation into
- * consideration; {@code false} otherwise. For the time being the only case where this is {@code
- * false} is when {@link #isFixedToUserRotation()} is {@code true}.
- */
- boolean respectAppRequestedOrientation() {
- return !isFixedToUserRotation();
- }
-
public int getLandscapeRotation() {
return mLandscapeRotation;
}
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index ec62ed4..3eac1be 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -31,10 +31,8 @@
import android.view.Display;
import android.view.IWindow;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.view.View;
-import com.android.internal.util.Preconditions;
import com.android.server.wm.WindowManagerInternal.IDragDropCallback;
import java.util.Objects;
@@ -70,26 +68,30 @@
@NonNull private AtomicReference<IDragDropCallback> mCallback = new AtomicReference<>(
new IDragDropCallback() {});
+ DragDropController(WindowManagerService service, Looper looper) {
+ mService = service;
+ mHandler = new DragHandler(service, looper);
+ }
+
boolean dragDropActiveLocked() {
return mDragState != null && !mDragState.isClosing();
}
+ boolean dragSurfaceRelinquished() {
+ return mDragState != null && mDragState.mRelinquishDragSurface;
+ }
+
void registerCallback(IDragDropCallback callback) {
Objects.requireNonNull(callback);
mCallback.set(callback);
}
- DragDropController(WindowManagerService service, Looper looper) {
- mService = service;
- mHandler = new DragHandler(service, looper);
- }
-
void sendDragStartedIfNeededLocked(WindowState window) {
mDragState.sendDragStartedIfNeededLocked(window);
}
- IBinder performDrag(SurfaceSession session, int callerPid, int callerUid, IWindow window,
- int flags, SurfaceControl surface, int touchSource, float touchX, float touchY,
+ IBinder performDrag(int callerPid, int callerUid, IWindow window, int flags,
+ SurfaceControl surface, int touchSource, float touchX, float touchY,
float thumbCenterX, float thumbCenterY, ClipData data) {
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "perform drag: win=" + window + " surface=" + surface + " flags=" +
@@ -157,6 +159,7 @@
return null;
}
+ final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
mDragState.mData = data;
mDragState.broadcastDragStartedLocked(touchX, touchY);
mDragState.overridePointerIconLocked(touchSource);
@@ -165,7 +168,6 @@
mDragState.mThumbOffsetY = thumbCenterY;
// Make the surface visible at the proper location
- final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag");
final SurfaceControl.Transaction transaction = mDragState.mTransaction;
@@ -229,6 +231,8 @@
}
mDragState.mDragResult = consumed;
+ mDragState.mRelinquishDragSurface = consumed
+ && mDragState.targetInterceptsGlobalDrag(callingWin);
mDragState.endDragLocked();
}
} finally {
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index b80ed6b..2ea4b57 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
@@ -101,6 +102,7 @@
ClipDescription mDataDescription;
int mTouchSource;
boolean mDragResult;
+ boolean mRelinquishDragSurface;
float mOriginalAlpha;
float mOriginalX, mOriginalY;
float mCurrentX, mCurrentY;
@@ -114,6 +116,10 @@
* without having a WM lock.
*/
volatile boolean mAnimationCompleted = false;
+ /**
+ * The display on which the drag is happening. If it goes into a different display this will
+ * be updated.
+ */
DisplayContent mDisplayContent;
@Nullable private ValueAnimator mAnimator;
@@ -141,7 +147,7 @@
mSurfaceControl = surface;
mFlags = flags;
mLocalWin = localWin;
- mNotifiedWindows = new ArrayList<WindowState>();
+ mNotifiedWindows = new ArrayList<>();
mTransaction = service.mTransactionFactory.get();
}
@@ -211,7 +217,8 @@
y = mCurrentY;
}
DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
- x, y, null, null, null, null, mDragResult);
+ x, y, mThumbOffsetX, mThumbOffsetY, null, null, null, null, null,
+ mDragResult);
try {
ws.mClient.dispatchDragEvent(evt);
} catch (RemoteException e) {
@@ -239,7 +246,9 @@
mInputSurface = null;
}
if (mSurfaceControl != null) {
- mTransaction.reparent(mSurfaceControl, null).apply();
+ if (!mRelinquishDragSurface) {
+ mTransaction.reparent(mSurfaceControl, null).apply();
+ }
mSurfaceControl = null;
}
if (mAnimator != null && !mAnimationCompleted) {
@@ -301,7 +310,9 @@
// Pause rotations before a drag.
ProtoLog.d(WM_DEBUG_ORIENTATION, "Pausing rotation during drag");
- mDisplayContent.getDisplayRotation().pause();
+ mService.mRoot.forAllDisplays(dc -> {
+ dc.getDisplayRotation().pause();
+ });
}
void tearDown() {
@@ -316,7 +327,9 @@
// Resume rotations after a drag.
ProtoLog.d(WM_DEBUG_ORIENTATION, "Resuming rotation after drag");
- mDisplayContent.getDisplayRotation().resume();
+ mService.mRoot.forAllDisplays(dc -> {
+ dc.getDisplayRotation().resume();
+ });
}
}
@@ -364,9 +377,9 @@
Slog.d(TAG_WM, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
}
- mDisplayContent.forAllWindows(w -> {
- sendDragStartedLocked(w, touchX, touchY, mDataDescription);
- }, false /* traverseTopToBottom */ );
+ mService.mRoot.forAllWindows(w -> {
+ sendDragStartedLocked(w, touchX, touchY, mDataDescription, mData);
+ }, false /* traverseTopToBottom */);
}
/* helper - send a ACTION_DRAG_STARTED event, if the
@@ -378,10 +391,12 @@
* process, so it's safe for the caller to call recycle() on the event afterwards.
*/
private void sendDragStartedLocked(WindowState newWin, float touchX, float touchY,
- ClipDescription desc) {
- if (mDragInProgress && isValidDropTarget(newWin)) {
+ ClipDescription desc, ClipData data) {
+ final boolean interceptsGlobalDrag = targetInterceptsGlobalDrag(newWin);
+ if (mDragInProgress && isValidDropTarget(newWin, interceptsGlobalDrag)) {
DragEvent event = obtainDragEvent(newWin, DragEvent.ACTION_DRAG_STARTED,
- touchX, touchY, null, desc, null, null, false);
+ touchX, touchY, mThumbOffsetX, mThumbOffsetY, null, desc,
+ interceptsGlobalDrag ? data : null, null, null, false);
try {
newWin.mClient.dispatchDragEvent(event);
// track each window that we've notified that the drag is starting
@@ -397,11 +412,11 @@
}
}
- private boolean isValidDropTarget(WindowState targetWin) {
+ private boolean isValidDropTarget(WindowState targetWin, boolean interceptsGlobalDrag) {
if (targetWin == null) {
return false;
}
- if (!targetWin.isPotentialDragTarget()) {
+ if (!targetWin.isPotentialDragTarget(interceptsGlobalDrag)) {
return false;
}
if ((mFlags & View.DRAG_FLAG_GLOBAL) == 0 || !targetWindowSupportsGlobalDrag(targetWin)) {
@@ -411,8 +426,9 @@
}
}
- return mCrossProfileCopyAllowed ||
- mSourceUserId == UserHandle.getUserId(targetWin.getOwningUid());
+ return interceptsGlobalDrag
+ || mCrossProfileCopyAllowed
+ || mSourceUserId == UserHandle.getUserId(targetWin.getOwningUid());
}
private boolean targetWindowSupportsGlobalDrag(WindowState targetWin) {
@@ -422,6 +438,13 @@
|| targetWin.mActivityRecord.mTargetSdk >= Build.VERSION_CODES.N;
}
+ /**
+ * @return whether the given window {@param targetWin} can intercept global drags.
+ */
+ public boolean targetInterceptsGlobalDrag(WindowState targetWin) {
+ return (targetWin.mAttrs.privateFlags & PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP) != 0;
+ }
+
/* helper - send a ACTION_DRAG_STARTED event only if the window has not
* previously been notified, i.e. it became visible after the drag operation
* was begun. This is a rare case.
@@ -435,7 +458,7 @@
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "need to send DRAG_STARTED to new window " + newWin);
}
- sendDragStartedLocked(newWin, mCurrentX, mCurrentY, mDataDescription);
+ sendDragStartedLocked(newWin, mCurrentX, mCurrentY, mDataDescription, mData);
}
}
@@ -512,7 +535,7 @@
}
// force DRAG_EXITED_EVENT if appropriate
DragEvent evt = obtainDragEvent(mTargetWindow, DragEvent.ACTION_DRAG_EXITED,
- 0, 0, null, null, null, null, false);
+ 0, 0, 0, 0, null, null, null, null, null, false);
mTargetWindow.mClient.dispatchDragEvent(evt);
if (myPid != mTargetWindow.mSession.mPid) {
evt.recycle();
@@ -523,7 +546,7 @@
Slog.d(TAG_WM, "sending DRAG_LOCATION to " + touchedWin);
}
DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DRAG_LOCATION,
- x, y, null, null, null, null, false);
+ x, y, mThumbOffsetX, mThumbOffsetY, null, null, null, null, null, false);
touchedWin.mClient.dispatchDragEvent(evt);
if (myPid != touchedWin.mSession.mPid) {
evt.recycle();
@@ -581,7 +604,9 @@
final int myPid = Process.myPid();
final IBinder token = touchedWin.mClient.asBinder();
final DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y,
- null, null, mData, dragAndDropPermissions, false);
+ mThumbOffsetX, mThumbOffsetY, null, null, mData,
+ targetInterceptsGlobalDrag(touchedWin) ? mSurfaceControl : null,
+ dragAndDropPermissions, false);
try {
touchedWin.mClient.dispatchDragEvent(evt);
@@ -606,15 +631,14 @@
return mDragInProgress;
}
- private static DragEvent obtainDragEvent(WindowState win, int action,
- float x, float y, Object localState,
- ClipDescription description, ClipData data,
- IDragAndDropPermissions dragAndDropPermissions,
- boolean result) {
+ private static DragEvent obtainDragEvent(WindowState win, int action, float x, float y,
+ float offsetX, float offsetY, Object localState, ClipDescription description,
+ ClipData data, SurfaceControl dragSurface,
+ IDragAndDropPermissions dragAndDropPermissions, boolean result) {
final float winX = win.translateToWindowX(x);
final float winY = win.translateToWindowY(y);
- return DragEvent.obtain(action, winX, winY, localState, description, data,
- dragAndDropPermissions, result);
+ return DragEvent.obtain(action, winX, winY, offsetX, offsetY, localState, description, data,
+ dragSurface, dragAndDropPermissions, result);
}
private ValueAnimator createReturnAnimationLocked() {
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 71eb18c..79b88d8 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
@@ -25,7 +26,10 @@
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
-import static android.view.WindowManager.TRANSIT_UNSET;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_UNSET;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
@@ -163,7 +167,7 @@
if (keyguardChanged) {
// Irrelevant to AOD.
- dismissDockedStackIfNeeded();
+ dismissMultiWindowModeForTaskIfNeeded(null /* currentTaskControllsingOcclusion */);
setKeyguardGoingAway(false);
if (keyguardShowing) {
mDismissalRequested = false;
@@ -199,9 +203,12 @@
1 /* keyguardGoingAway */,
"keyguardGoingAway");
mRootWindowContainer.getDefaultDisplay()
- .prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
+ .prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
false /* alwaysKeepCurrent */, convertTransitFlags(flags),
false /* forceOverride */);
+ mRootWindowContainer.getDefaultDisplay()
+ .prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
+ convertTransitFlags(flags));
updateKeyguardSleepToken();
// Some stack visibility might change (e.g. docked stack)
@@ -328,8 +335,12 @@
/**
* Called when occluded state changed.
+ *
+ * @param currentTaskControllingOcclusion the task that controls the state whether keyguard
+ * should be occluded. That is the task to be shown on top of keyguard if it requests so.
*/
- private void handleOccludedChanged(int displayId) {
+ private void handleOccludedChanged(
+ int displayId, @Nullable Task currentTaskControllingOcclusion) {
// TODO(b/113840485): Handle app transition for individual display, and apply occluded
// state change to secondary displays.
// For now, only default display fully supports occluded change. Other displays only
@@ -344,16 +355,21 @@
mService.deferWindowLayout();
try {
mRootWindowContainer.getDefaultDisplay()
- .prepareAppTransition(resolveOccludeTransit(),
+ .prepareAppTransitionOld(resolveOccludeTransit(),
false /* alwaysKeepCurrent */, 0 /* flags */,
true /* forceOverride */);
+ mRootWindowContainer.getDefaultDisplay()
+ .prepareAppTransition(
+ isDisplayOccluded(DEFAULT_DISPLAY)
+ ? TRANSIT_KEYGUARD_OCCLUDE
+ : TRANSIT_KEYGUARD_UNOCCLUDE);
updateKeyguardSleepToken(DEFAULT_DISPLAY);
mWindowManager.executeAppTransition();
} finally {
mService.continueWindowLayout();
}
}
- dismissDockedStackIfNeeded();
+ dismissMultiWindowModeForTaskIfNeeded(currentTaskControllingOcclusion);
}
/**
@@ -374,9 +390,10 @@
// we immediately dismiss the Keyguard so the activity gets shown without a flicker.
final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
if (mKeyguardShowing && canDismissKeyguard()
- && dc.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
- dc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */,
+ && dc.mAppTransition.getAppTransitionOld() == TRANSIT_OLD_KEYGUARD_UNOCCLUDE) {
+ dc.prepareAppTransitionOld(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */,
0 /* flags */, true /* forceOverride */);
+ dc.prepareAppTransition(TRANSIT_KEYGUARD_UNOCCLUDE);
mWindowManager.executeAppTransition();
}
}
@@ -394,9 +411,10 @@
}
private int resolveOccludeTransit() {
+ // TODO(new-app-transition): Remove after migrating to the enw transit system.
final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
- if (mBeforeUnoccludeTransit != TRANSIT_UNSET
- && dc.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
+ if (mBeforeUnoccludeTransit != TRANSIT_OLD_UNSET
+ && dc.mAppTransition.getAppTransitionOld() == TRANSIT_OLD_KEYGUARD_UNOCCLUDE
// TODO(b/113840485): Handle app transition for individual display.
&& isDisplayOccluded(DEFAULT_DISPLAY)) {
@@ -407,27 +425,38 @@
} else if (!isDisplayOccluded(DEFAULT_DISPLAY)) {
// Save transit in case we dismiss/occlude Keyguard shortly after.
- mBeforeUnoccludeTransit = dc.mAppTransition.getAppTransition();
- return TRANSIT_KEYGUARD_UNOCCLUDE;
+ mBeforeUnoccludeTransit = dc.mAppTransition.getAppTransitionOld();
+ return TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
} else {
- return TRANSIT_KEYGUARD_OCCLUDE;
+ return TRANSIT_OLD_KEYGUARD_OCCLUDE;
}
}
- private void dismissDockedStackIfNeeded() {
+ private void dismissMultiWindowModeForTaskIfNeeded(
+ @Nullable Task currentTaskControllingOcclusion) {
// TODO(b/113840485): Handle docked stack for individual display.
- if (mKeyguardShowing && isDisplayOccluded(DEFAULT_DISPLAY)) {
- // The lock screen is currently showing, but is occluded by a window that can
- // show on top of the lock screen. In this can we want to dismiss the docked
- // stack since it will be complicated/risky to try to put the activity on top
- // of the lock screen in the right fullscreen configuration.
- final TaskDisplayArea taskDisplayArea = mRootWindowContainer
- .getDefaultTaskDisplayArea();
- if (!taskDisplayArea.isSplitScreenModeActivated()) {
- return;
- }
+ if (!mKeyguardShowing || !isDisplayOccluded(DEFAULT_DISPLAY)) {
+ return;
+ }
+
+ // Dismiss split screen
+
+ // The lock screen is currently showing, but is occluded by a window that can
+ // show on top of the lock screen. In this can we want to dismiss the docked
+ // stack since it will be complicated/risky to try to put the activity on top
+ // of the lock screen in the right fullscreen configuration.
+ final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+ if (taskDisplayArea.isSplitScreenModeActivated()) {
taskDisplayArea.onSplitScreenModeDismissed();
}
+
+ // Dismiss freeform windowing mode
+ if (currentTaskControllingOcclusion == null) {
+ return;
+ }
+ if (currentTaskControllingOcclusion.inFreeformWindowingMode()) {
+ currentTaskControllingOcclusion.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ }
}
private void updateKeyguardSleepToken() {
@@ -559,7 +588,7 @@
}
if (lastOccluded != mOccluded) {
- controller.handleOccludedChanged(mDisplayId);
+ controller.handleOccludedChanged(mDisplayId, task);
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 1cf50ab..ca429f8 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -23,7 +23,6 @@
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.WindowManager.TRANSIT_NONE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -221,7 +220,7 @@
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Moved stack=%s behind stack=%s",
targetStack, getStackAbove(targetStack));
- mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
+ mWindowManager.prepareAppTransitionNone();
mWindowManager.executeAppTransition();
// TODO: Maybe wait for app to draw in this particular case?
@@ -377,7 +376,7 @@
return;
}
- mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
+ mWindowManager.prepareAppTransitionNone();
mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, false);
mService.mRootWindowContainer.resumeFocusedStacksTopActivities();
@@ -436,7 +435,7 @@
|| controller.isTargetApp(stack.getTopNonFinishingActivity()))
&& controller.shouldDeferCancelUntilNextTransition()) {
// Always prepare an app transition since we rely on the transition callbacks to cleanup
- mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
+ mWindowManager.prepareAppTransitionNone();
controller.setCancelOnNextTransitionStart();
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 4ad2575..1736ac9 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -214,6 +214,26 @@
}
@Override
+ public void setFinishTaskBounds(int taskId, Rect destinationBounds) {
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
+ "setFinishTaskBounds(%d): bounds=%s", taskId, destinationBounds);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mService.getWindowManagerLock()) {
+ for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+ final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
+ if (taskAdapter.mTask.mTaskId == taskId) {
+ taskAdapter.mFinishBounds.set(destinationBounds);
+ break;
+ }
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void finish(boolean moveHomeToTop, boolean sendUserLeaveHint) {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
"finish(%b): mCanceled=%b", moveHomeToTop, mCanceled);
@@ -739,6 +759,7 @@
taskAdapter.mTask.dontAnimateDimExit();
}
removeAnimation(taskAdapter);
+ taskAdapter.maybeApplyFinishBounds();
}
for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
@@ -925,7 +946,9 @@
private RemoteAnimationTarget mTarget;
private final Rect mBounds = new Rect();
// The bounds of the target relative to its parent.
- private Rect mLocalBounds = new Rect();
+ private final Rect mLocalBounds = new Rect();
+ // The bounds of the target when animation is finished
+ private final Rect mFinishBounds = new Rect();
TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) {
mTask = task;
@@ -960,6 +983,17 @@
return mTarget;
}
+ void maybeApplyFinishBounds() {
+ if (!mFinishBounds.isEmpty()) {
+ final SurfaceControl taskSurface = mTask.getSurfaceControl();
+ mTask.getPendingTransaction()
+ .setPosition(taskSurface, mFinishBounds.left, mFinishBounds.top)
+ .setWindowCrop(taskSurface, mFinishBounds.width(), mFinishBounds.height())
+ .apply();
+ mFinishBounds.setEmpty();
+ }
+ }
+
@Override
public boolean getShowWallpaper() {
return false;
@@ -1005,6 +1039,7 @@
}
pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible);
pw.println("mLocalBounds=" + mLocalBounds);
+ pw.println("mFinishBounds=" + mFinishBounds);
pw.println("mBounds=" + mBounds);
pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible);
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 11718dc..04b3030 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -36,9 +36,12 @@
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
-import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
+import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_KEEP_SCREEN_ON;
@@ -52,6 +55,7 @@
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
@@ -511,6 +515,7 @@
void onChildPositionChanged(WindowContainer child) {
mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
!mWmService.mPerDisplayFocusEnabled /* updateInputWindows */);
+ mStackSupervisor.updateTopResumedActivityIfNeeded();
}
/**
@@ -2175,7 +2180,8 @@
// Set a transition to ensure that we don't immediately try and update the visibility
// of the activity entering PIP
- r.getDisplayContent().prepareAppTransition(TRANSIT_NONE, false);
+ r.getDisplayContent().prepareAppTransitionOld(TRANSIT_OLD_NONE, false);
+ r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);
final boolean singleActivity = task.getChildCount() == 1;
final Task stack;
@@ -2206,8 +2212,8 @@
// to the list of apps being closed, and request its transition to be ran.
final ActivityRecord oldTopActivity = task.getTopMostActivity();
if (oldTopActivity != null && oldTopActivity.isState(STOPPED)
- && task.getDisplayContent().mAppTransition.getAppTransition()
- == TRANSIT_TASK_TO_BACK) {
+ && task.getDisplayContent().mAppTransition.getAppTransitionOld()
+ == TRANSIT_OLD_TASK_TO_BACK) {
task.getDisplayContent().mClosingApps.add(oldTopActivity);
oldTopActivity.mRequestForceTransition = true;
}
@@ -2626,12 +2632,6 @@
+ " in=" + taskDisplayArea);
}
- @Override
- void positionChildAt(int position, DisplayContent child, boolean includingParents) {
- super.positionChildAt(position, child, includingParents);
- mStackSupervisor.updateTopResumedActivityIfNeeded();
- }
-
Configuration getDisplayOverrideConfiguration(int displayId) {
final DisplayContent displayContent = getDisplayContentOrCreate(displayId);
if (displayContent == null) {
@@ -2780,7 +2780,8 @@
if (allowDelay) {
result &= stack.goToSleepIfPossible(shuttingDown);
} else {
- stack.goToSleep();
+ stack.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ !PRESERVE_WINDOWS);
}
}
return result;
@@ -2799,8 +2800,9 @@
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
r.detachFromProcess();
- r.mDisplayContent.prepareAppTransition(
- TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
+ r.mDisplayContent.prepareAppTransitionOld(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE,
+ false /* alwaysKeepCurrent */);
+ r.mDisplayContent.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
r.destroyIfPossible("handleAppCrashed");
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 70dbf68..6fbd351 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -19,6 +19,13 @@
import static android.Manifest.permission.DEVICE_POWER;
import static android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
+import static android.content.Intent.EXTRA_SHORTCUT_ID;
+import static android.content.Intent.EXTRA_TASK_ID;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -30,7 +37,12 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.Nullable;
+import android.app.ActivityManagerInternal;
+import android.app.PendingIntent;
import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
@@ -42,6 +54,7 @@
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.MergedConfiguration;
import android.util.Slog;
@@ -58,8 +71,10 @@
import android.view.WindowManager;
import android.window.ClientWindowFrames;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.LocalServices;
import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
@@ -272,15 +287,101 @@
@Override
public IBinder performDrag(IWindow window, int flags, SurfaceControl surface, int touchSource,
float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data) {
+ // Validate and resolve ClipDescription data before clearing the calling identity
+ validateAndResolveDragMimeTypeExtras(data, Binder.getCallingUid());
final long ident = Binder.clearCallingIdentity();
try {
- return mDragDropController.performDrag(mSurfaceSession, mPid, mUid, window,
- flags, surface, touchSource, touchX, touchY, thumbCenterX, thumbCenterY, data);
+ return mDragDropController.performDrag(mPid, mUid, window, flags, surface, touchSource,
+ touchX, touchY, thumbCenterX, thumbCenterY, data);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
+ /**
+ * Validates the given drag data.
+ */
+ @VisibleForTesting
+ public void validateAndResolveDragMimeTypeExtras(ClipData data, int callingUid) {
+ if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+ throw new IllegalStateException("Need to validate before calling identify is cleared");
+ }
+ final ClipDescription desc = data != null ? data.getDescription() : null;
+ if (desc == null) {
+ return;
+ }
+ // Ensure that only one of the app mime types are set
+ final boolean hasActivity = desc.hasMimeType(MIMETYPE_APPLICATION_ACTIVITY);
+ final boolean hasShortcut = desc.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
+ final boolean hasTask = desc.hasMimeType(MIMETYPE_APPLICATION_TASK);
+ int appMimeTypeCount = (hasActivity ? 1 : 0)
+ + (hasShortcut ? 1 : 0)
+ + (hasTask ? 1 : 0);
+ if (appMimeTypeCount == 0) {
+ return;
+ } else if (appMimeTypeCount > 1) {
+ throw new IllegalArgumentException("Can not specify more than one of activity, "
+ + "shortcut, or task mime types");
+ }
+ // Ensure that data is provided and that they are intents
+ if (data.getItemCount() == 0) {
+ throw new IllegalArgumentException("Unexpected number of items (none)");
+ }
+ for (int i = 0; i < data.getItemCount(); i++) {
+ if (data.getItemAt(i).getIntent() == null) {
+ throw new IllegalArgumentException("Unexpected item, expected an intent");
+ }
+ }
+
+ if (hasActivity) {
+ long origId = Binder.clearCallingIdentity();
+ try {
+ // Resolve the activity info for each intent
+ for (int i = 0; i < data.getItemCount(); i++) {
+ final ClipData.Item item = data.getItemAt(i);
+ final Intent intent = item.getIntent();
+ final PendingIntent pi = intent.getParcelableExtra(
+ ClipDescription.EXTRA_PENDING_INTENT);
+ final UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
+ if (pi == null || user == null) {
+ throw new IllegalArgumentException("Clip data must include the pending "
+ + "intent to launch and its associated user to launch for.");
+ }
+ final Intent launchIntent = mService.mAmInternal.getIntentForIntentSender(
+ pi.getIntentSender().getTarget());
+ final ActivityInfo info = mService.mAtmService.resolveActivityInfoForIntent(
+ launchIntent, null /* resolvedType */, user.getIdentifier(),
+ callingUid);
+ item.setActivityInfo(info);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ } else if (hasShortcut) {
+ mService.mAtmService.enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS,
+ "performDrag");
+ for (int i = 0; i < data.getItemCount(); i++) {
+ final Intent intent = data.getItemAt(i).getIntent();
+ final UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
+ if (!intent.hasExtra(EXTRA_SHORTCUT_ID)
+ || TextUtils.isEmpty(intent.getStringExtra(EXTRA_SHORTCUT_ID))
+ || user == null) {
+ throw new IllegalArgumentException("Clip item must include the shortcut id and "
+ + "the user to launch for.");
+ }
+ }
+ } else if (hasTask) {
+ mService.mAtmService.enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS,
+ "performDrag");
+ for (int i = 0; i < data.getItemCount(); i++) {
+ final Intent intent = data.getItemAt(i).getIntent();
+ if (intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID) == INVALID_TASK_ID) {
+ throw new IllegalArgumentException("Clip item must include the task id.");
+ }
+ }
+ }
+ }
+
@Override
public void reportDropResult(IWindow window, boolean consumed) {
final long ident = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1cee9c6..ef1a3be 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -63,16 +63,23 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.SurfaceControl.METADATA_TASK_ID;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_CHANGE_WINDOWING_MODE;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
-import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
-import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
-import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
@@ -113,6 +120,7 @@
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.Task.ActivityState.PAUSED;
import static com.android.server.wm.Task.ActivityState.PAUSING;
import static com.android.server.wm.Task.ActivityState.RESUMED;
@@ -520,6 +528,11 @@
// {@link ActivityInfo#FLAG_SUPPORTS_PICTURE_IN_PICTURE} flag of the root activity.
boolean mSupportsPictureInPicture;
+ // Activity bounds if this task or its top activity is presented in letterbox mode and
+ // {@code null} otherwise.
+ @Nullable
+ private Rect mLetterboxActivityBounds;
+
// Whether the task is currently being drag-resized
private boolean mDragResizing;
private int mDragResizeMode;
@@ -782,6 +795,10 @@
*/
boolean mTaskAppearedSent;
+ // If the sending of the task appear signal should be deferred until this flag is set back to
+ // false.
+ private boolean mDeferTaskAppear;
+
/**
* This task was created by the task organizer which has the following implementations.
* <ul>
@@ -794,14 +811,20 @@
@VisibleForTesting
boolean mCreatedByOrganizer;
+ // Tracking cookie for the creation of this task.
+ IBinder mLaunchCookie;
+
/**
* Don't use constructor directly. Use {@link TaskDisplayArea#createStackUnchecked()} instead.
*/
- Task(ActivityTaskManagerService atmService, int id, int activityType,
- ActivityInfo info, Intent intent, boolean createdByOrganizer) {
+ Task(ActivityTaskManagerService atmService, int id, int activityType, ActivityInfo info,
+ Intent intent, boolean createdByOrganizer, boolean deferTaskAppear,
+ IBinder launchCookie) {
this(atmService, id, info, intent, null /*voiceSession*/, null /*voiceInteractor*/,
null /*taskDescription*/, null /*stack*/);
mCreatedByOrganizer = createdByOrganizer;
+ mLaunchCookie = launchCookie;
+ mDeferTaskAppear = deferTaskAppear;
setActivityType(activityType);
}
@@ -2359,8 +2382,9 @@
* Initializes a change transition. See {@link SurfaceFreezer} for more information.
*/
private void initializeChangeTransition(Rect startBounds) {
- mDisplayContent.prepareAppTransition(TRANSIT_TASK_CHANGE_WINDOWING_MODE,
+ mDisplayContent.prepareAppTransitionOld(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE,
false /* alwaysKeepCurrent */, 0, false /* forceOverride */);
+ mDisplayContent.prepareAppTransition(TRANSIT_CHANGE_WINDOWING_MODE);
mAtmService.getTransitionController().collect(this);
mDisplayContent.mChangingContainers.add(this);
@@ -2445,7 +2469,7 @@
@Override
public SurfaceControl getFreezeSnapshotTarget() {
- final int transit = mDisplayContent.mAppTransition.getAppTransition();
+ final int transit = mDisplayContent.mAppTransition.getAppTransitionOld();
if (!AppTransition.isChangeTransit(transit)) {
return null;
}
@@ -2812,16 +2836,25 @@
int windowingMode =
getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
+ final int parentWindowingMode = newParentConfig.windowConfiguration.getWindowingMode();
// Resolve override windowing mode to fullscreen for home task (even on freeform
// display), or split-screen if in split-screen mode.
if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) {
- final int parentWindowingMode = newParentConfig.windowConfiguration.getWindowingMode();
windowingMode = WindowConfiguration.isSplitScreenWindowingMode(parentWindowingMode)
? parentWindowingMode : WINDOWING_MODE_FULLSCREEN;
getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
}
+ // Do not allow non-resizable non-pinned tasks to be in a multi-window mode - they should
+ // use their parent's windowing mode, or fullscreen.
+ if (!isResizeable() && windowingMode != WINDOWING_MODE_PINNED
+ && WindowConfiguration.inMultiWindowMode(windowingMode)) {
+ windowingMode = WindowConfiguration.inMultiWindowMode(parentWindowingMode)
+ ? WINDOWING_MODE_FULLSCREEN : parentWindowingMode;
+ getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
+ }
+
if (isLeafTask()) {
resolveLeafOnlyOverrideConfigs(newParentConfig, mTmpBounds /* previousBounds */);
}
@@ -2905,15 +2938,34 @@
return;
}
+ if (refActivity != null && refActivity.hasCompatDisplayInsets()) {
+ // App prefers to keep its original size.
+ return;
+ }
+
final int parentWidth = parentBounds.width();
final int parentHeight = parentBounds.height();
- final float aspect = ((float) parentHeight) / parentWidth;
+ float aspect = Math.max(parentWidth, parentHeight)
+ / (float) Math.min(parentWidth, parentHeight);
+
+ // Adjust the Task letterbox bounds to fit the app request aspect ratio in order to use the
+ // extra available space.
+ if (refActivity != null) {
+ final float maxAspectRatio = refActivity.info.maxAspectRatio;
+ final float minAspectRatio = refActivity.info.minAspectRatio;
+ if (aspect > maxAspectRatio && maxAspectRatio != 0) {
+ aspect = maxAspectRatio;
+ } else if (aspect < minAspectRatio) {
+ aspect = minAspectRatio;
+ }
+ }
+
if (forcedOrientation == ORIENTATION_LANDSCAPE) {
- final int height = (int) (parentWidth / aspect);
+ final int height = (int) Math.rint(parentWidth / aspect);
final int top = parentBounds.centerY() - height / 2;
outBounds.set(parentBounds.left, top, parentBounds.right, top + height);
} else {
- final int width = (int) (parentHeight * aspect);
+ final int width = (int) Math.rint(parentHeight / aspect);
final int left = parentBounds.centerX() - width / 2;
outBounds.set(left, parentBounds.top, left + width, parentBounds.bottom);
}
@@ -3269,7 +3321,7 @@
@Override
public boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
- ConfigurationContainer requestingContainer) {
+ WindowContainer requestingContainer) {
if (super.onDescendantOrientationChanged(freezeDisplayToken, requestingContainer)) {
return true;
}
@@ -3277,6 +3329,18 @@
// No one in higher hierarchy handles this request, let's adjust our bounds to fulfill
// it if possible.
if (getParent() != null) {
+ final ActivityRecord activity = requestingContainer.asActivityRecord();
+ if (activity != null) {
+ // Clear the size compat cache to recompute the bounds for requested orientation;
+ // otherwise when Task#computeFullscreenBounds(), it will not try to do Task level
+ // letterboxing because app may prefer to keep its original size (size compat).
+ //
+ // Normally, ActivityRecord#clearSizeCompatMode() recomputes from its parent Task,
+ // which is the leaf Task. However, because this orientation request is new to all
+ // Tasks, pass false to clearSizeCompatMode, and trigger onConfigurationChanged from
+ // here (root Task) to make sure all Tasks are up-to-date.
+ activity.clearSizeCompatMode(false /* recomputeTask */);
+ }
onConfigurationChanged(getParent().getConfiguration());
return true;
}
@@ -3307,7 +3371,9 @@
}
boolean isResizeable(boolean checkSupportsPip) {
- return (mAtmService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode)
+ final boolean forceResizable = mAtmService.mForceResizableActivities
+ && getActivityType() == ACTIVITY_TYPE_STANDARD;
+ return (forceResizable || ActivityInfo.isResizeableMode(mResizeMode)
|| (checkSupportsPip && mSupportsPictureInPicture));
}
@@ -3656,14 +3722,9 @@
super.setInitialSurfaceControlProperties(b);
}
- boolean isTaskAnimating() {
- final RecentsAnimationController recentsAnim = mWmService.getRecentsAnimationController();
- if (recentsAnim != null) {
- if (recentsAnim.isAnimatingTask(this)) {
- return true;
- }
- }
- return forAllTasks((t) -> { return t != this && t.isTaskAnimating(); });
+ /** Checking if self or its child tasks are animated by recents animation. */
+ boolean isAnimatingByRecents() {
+ return isAnimating(CHILDREN, ANIMATION_TYPE_RECENTS);
}
@Override
@@ -3794,6 +3855,10 @@
|| activityType == ACTIVITY_TYPE_ASSISTANT;
}
+ boolean isTaskLetterboxed() {
+ return getWindowingMode() == WINDOWING_MODE_FULLSCREEN && !matchParentBounds();
+ }
+
@Override
boolean fillsParent() {
// From the perspective of policy, we still want to report that this task fills parent
@@ -3940,10 +4005,10 @@
if (control != null) {
// We let the transition to be controlled by RecentsAnimation, and callback task's
// RemoteAnimationTarget for remote runner to animate.
- if (enter) {
+ if (enter && !isHomeOrRecentsStack()) {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
"applyAnimationUnchecked, control: %s, task: %s, transit: %s",
- control, asTask(), AppTransition.appTransitionToString(transit));
+ control, asTask(), AppTransition.appTransitionOldToString(transit));
control.addTaskToTargets(this, (type, anim) -> {
for (int i = 0; i < sources.size(); ++i) {
sources.get(i).onAnimationFinished(type, anim);
@@ -4033,11 +4098,18 @@
info.resizeMode = top != null ? top.mResizeMode : mResizeMode;
info.topActivityType = top.getActivityType();
info.isResizeable = isResizeable();
+ // Don't query getTopNonFinishingActivity().getBounds() directly because when fillTaskInfo
+ // is triggered for the first time after activities change, getBounds() may return non final
+ // bounds, e.g. fullscreen bounds instead of letterboxed bounds. To work around this,
+ // assigning bounds from ActivityRecord#layoutLetterbox when they are ready.
+ info.letterboxActivityBounds = Rect.copyOrNull(mLetterboxActivityBounds);
+ info.positionInParent = getRelativePosition();
info.pictureInPictureParams = getPictureInPictureParams();
info.topActivityInfo = mReuseActivitiesReport.top != null
? mReuseActivitiesReport.top.info
: null;
+ info.addLaunchCookie(mLaunchCookie);
forAllActivities(r -> {
info.addLaunchCookie(r.mLaunchCookie);
});
@@ -4051,6 +4123,21 @@
? null : rootActivity.pictureInPictureArgs;
}
+ void maybeUpdateLetterboxBounds(
+ ActivityRecord activityRecord, @Nullable Rect letterboxActivityBounds) {
+ if (isOrganized()
+ && mReuseActivitiesReport.top == activityRecord
+ // Want to force update only if letterbox bounds have changed.
+ && !Objects.equals(
+ mLetterboxActivityBounds,
+ letterboxActivityBounds)) {
+ mLetterboxActivityBounds = Rect.copyOrNull(letterboxActivityBounds);
+ // Forcing update to reduce visual jank during the transition.
+ mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
+ this, /* force= */ true);
+ }
+ }
+
/**
* Returns a {@link TaskInfo} with information from this task.
*/
@@ -4792,6 +4879,13 @@
return mHasBeenVisible;
}
+ void setDeferTaskAppear(boolean deferTaskAppear) {
+ mDeferTaskAppear = deferTaskAppear;
+ if (!mDeferTaskAppear) {
+ sendTaskAppeared();
+ }
+ }
+
/** In the case that these conditions are true, we want to send the Task to the organizer:
* 1. An organizer has been set
* 2. The Task was created by the organizer
@@ -4806,6 +4900,10 @@
return false;
}
+ if (mDeferTaskAppear) {
+ return false;
+ }
+
if (mCreatedByOrganizer) {
return true;
}
@@ -5239,14 +5337,12 @@
taskDisplayArea.moveHomeStackToFront(reason + " returnToHome");
}
- if (isRootTask()) {
- taskDisplayArea.positionChildAt(POSITION_TOP, this, false /* includingParents */,
- reason);
- }
+ final Task lastFocusedTask = isRootTask() ? taskDisplayArea.getFocusedStack() : null;
if (task == null) {
task = this;
}
task.getParent().positionChildAt(POSITION_TOP, task, true /* includingParents */);
+ taskDisplayArea.updateLastFocusedRootTask(lastFocusedTask, reason);
}
/**
@@ -5269,8 +5365,9 @@
if (parentTask != null) {
parentTask.moveToBack(reason, this);
} else {
- displayArea.positionChildAt(POSITION_BOTTOM, this, false /*includingParents*/,
- reason);
+ final Task lastFocusedTask = displayArea.getFocusedStack();
+ displayArea.positionChildAt(POSITION_BOTTOM, this, false /*includingParents*/);
+ displayArea.updateLastFocusedRootTask(lastFocusedTask, reason);
}
if (task != null && task != this) {
positionChildAtBottom(task);
@@ -5317,8 +5414,6 @@
}
void awakeFromSleepingLocked() {
- // Ensure activities are no longer sleeping.
- forAllActivities((Consumer<ActivityRecord>) (r) -> r.setSleeping(false));
if (mPausingActivity != null) {
Slog.d(TAG, "awakeFromSleepingLocked: previously pausing activity didn't pause");
mPausingActivity.activityPaused(true);
@@ -5372,27 +5467,13 @@
}
if (shouldSleep) {
- goToSleep();
+ ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ !PRESERVE_WINDOWS);
}
return shouldSleep;
}
- void goToSleep() {
- // Make sure all visible activities are now sleeping. This will update the activity's
- // visibility and onStop() will be called.
- forAllActivities((r) -> {
- if (r.isState(STARTED, RESUMED, PAUSING, PAUSED, STOPPING, STOPPED)) {
- r.setSleeping(true);
- }
- });
-
- // Ensure visibility after updating sleep states without updating configuration,
- // as activities are about to be sent to sleep.
- ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- !PRESERVE_WINDOWS);
- }
-
private boolean containsActivityFromStack(List<ActivityRecord> rs) {
for (ActivityRecord r : rs) {
if (r.getRootTask() == this) {
@@ -5689,8 +5770,9 @@
boolean preserveWindows, boolean notifyClients) {
mStackSupervisor.beginActivityVisibilityUpdate();
try {
- mEnsureActivitiesVisibleHelper.process(starting, configChanges, preserveWindows,
- notifyClients);
+ forAllLeafTasks(task -> task.mEnsureActivitiesVisibleHelper.process(
+ starting, configChanges, preserveWindows, notifyClients),
+ true /* traverseTopToBottom */);
if (mTranslucentActivityWaiting != null &&
mUndrawnActivitiesBelowTopTranslucent.isEmpty()) {
@@ -5877,6 +5959,8 @@
if (mResumedActivity == next && next.isState(RESUMED)
&& taskDisplayArea.getWindowingMode() != WINDOWING_MODE_FREEFORM
&& taskDisplayArea.allResumedActivitiesComplete()) {
+ // The activity may be waiting for stop, but that is no longer appropriate for it.
+ mStackSupervisor.mStoppingActivities.remove(next);
// Make sure we have executed any pending transitions, since there
// should be nothing left to do at this point.
executeAppTransition(options);
@@ -5923,7 +6007,6 @@
// The activity may be waiting for stop, but that is no longer
// appropriate for it.
mStackSupervisor.mStoppingActivities.remove(next);
- next.setSleeping(false);
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
@@ -6052,11 +6135,13 @@
"Prepare close transition: prev=" + prev);
if (mStackSupervisor.mNoAnimActivities.contains(prev)) {
anim = false;
- dc.prepareAppTransition(TRANSIT_NONE, false);
+ dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, false);
+ dc.prepareAppTransition(TRANSIT_NONE);
} else {
- dc.prepareAppTransition(
- prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_CLOSE
- : TRANSIT_TASK_CLOSE, false);
+ dc.prepareAppTransitionOld(
+ prev.getTask() == next.getTask() ? TRANSIT_OLD_ACTIVITY_CLOSE
+ : TRANSIT_OLD_TASK_CLOSE, false);
+ dc.prepareAppTransition(TRANSIT_CLOSE);
}
prev.setVisibility(false);
} else {
@@ -6064,21 +6149,25 @@
"Prepare open transition: prev=" + prev);
if (mStackSupervisor.mNoAnimActivities.contains(next)) {
anim = false;
- dc.prepareAppTransition(TRANSIT_NONE, false);
+ dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, false);
+ dc.prepareAppTransition(TRANSIT_NONE);
} else {
- dc.prepareAppTransition(
- prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_OPEN
- : next.mLaunchTaskBehind ? TRANSIT_TASK_OPEN_BEHIND
- : TRANSIT_TASK_OPEN, false);
+ dc.prepareAppTransitionOld(
+ prev.getTask() == next.getTask() ? TRANSIT_OLD_ACTIVITY_OPEN
+ : next.mLaunchTaskBehind ? TRANSIT_OLD_TASK_OPEN_BEHIND
+ : TRANSIT_OLD_TASK_OPEN, /* alwaysKeepCurrent */false);
+ dc.prepareAppTransition(TRANSIT_OPEN);
}
}
} else {
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
if (mStackSupervisor.mNoAnimActivities.contains(next)) {
anim = false;
- dc.prepareAppTransition(TRANSIT_NONE, false);
+ dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, false);
+ dc.prepareAppTransition(TRANSIT_NONE);
} else {
- dc.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false);
+ dc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN, false);
+ dc.prepareAppTransition(TRANSIT_OPEN);
}
}
@@ -6190,7 +6279,6 @@
EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next),
next.getTask().mTaskId, next.shortComponentName);
- next.setSleeping(false);
mAtmService.getAppWarningsLocked().onResumeActivity(next);
next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState);
next.clearOptionsLocked();
@@ -6337,13 +6425,14 @@
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
"Prepare open transition: starting " + r);
if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- dc.prepareAppTransition(TRANSIT_NONE, keepCurTransition);
+ dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, keepCurTransition);
+ dc.prepareAppTransition(TRANSIT_NONE);
mStackSupervisor.mNoAnimActivities.add(r);
} else {
- int transit = TRANSIT_ACTIVITY_OPEN;
+ int transit = TRANSIT_OLD_ACTIVITY_OPEN;
if (newTask) {
if (r.mLaunchTaskBehind) {
- transit = TRANSIT_TASK_OPEN_BEHIND;
+ transit = TRANSIT_OLD_TASK_OPEN_BEHIND;
} else {
// If a new task is being launched, then mark the existing top activity as
// supporting picture-in-picture while pausing only if the starting activity
@@ -6353,17 +6442,18 @@
null /* toFrontTask */, r, options)) {
focusedTopActivity.supportsEnterPipOnTaskSwitch = true;
}
- transit = TRANSIT_TASK_OPEN;
+ transit = TRANSIT_OLD_TASK_OPEN;
}
}
if (mAtmService.getTransitionController().isShellTransitionsEnabled()
// TODO(shell-transitions): eventually all transitions.
- && transit == TRANSIT_TASK_OPEN) {
+ && transit == TRANSIT_OLD_TASK_OPEN) {
Transition transition =
mAtmService.getTransitionController().requestTransition(transit);
transition.collect(task);
} else {
- dc.prepareAppTransition(transit, keepCurTransition);
+ dc.prepareAppTransitionOld(transit, keepCurTransition);
+ dc.prepareAppTransition(TRANSIT_OPEN);
}
mStackSupervisor.mNoAnimActivities.remove(r);
}
@@ -6502,8 +6592,9 @@
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
Task finishedTask = r.getTask();
- mDisplayContent.prepareAppTransition(
- TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
+ mDisplayContent.prepareAppTransitionOld(
+ TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
+ mDisplayContent.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
r.finishIfPossible(reason, false /* oomAdj */);
// Also terminate any activities below it that aren't yet stopped, to avoid a situation
@@ -6734,7 +6825,9 @@
forAllActivities(ActivityRecord::removeLaunchTickRunnable);
}
- private void updateTransitLocked(int transit, ActivityOptions options, boolean forceOverride) {
+ private void updateTransitLocked(@WindowManager.TransitionOldType int transit,
+ @WindowManager.TransitionType int transit2, ActivityOptions options,
+ boolean forceOverride) {
if (options != null) {
ActivityRecord r = topRunningActivity();
if (r != null && !r.isState(RESUMED)) {
@@ -6743,8 +6836,9 @@
ActivityOptions.abort(options);
}
}
- mDisplayContent.prepareAppTransition(transit, false,
+ mDisplayContent.prepareAppTransitionOld(transit, false,
0 /* flags */, forceOverride);
+ mDisplayContent.prepareAppTransition(transit2);
}
final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
@@ -6765,7 +6859,8 @@
if (noAnimation) {
ActivityOptions.abort(options);
} else {
- updateTransitLocked(TRANSIT_TASK_TO_FRONT, options, false /* forceOverride */);
+ updateTransitLocked(TRANSIT_OLD_TASK_TO_FRONT, TRANSIT_TO_FRONT, options,
+ false /* forceOverride */);
}
return;
}
@@ -6784,13 +6879,10 @@
// get calculated incorrectly.
mDisplayContent.deferUpdateImeTarget();
- // Shift all activities with this task up to the top
- // of the stack, keeping them in the same internal order.
- positionChildAtTop(tr);
-
// Don't refocus if invisible to current user
final ActivityRecord top = tr.getTopNonFinishingActivity();
if (top == null || !top.okToShowLocked()) {
+ positionChildAtTop(tr);
if (top != null) {
mStackSupervisor.mRecentTasks.add(top.getTask());
}
@@ -6798,21 +6890,19 @@
return;
}
- // Set focus to the top running activity of this stack.
- final ActivityRecord r = topRunningActivity();
- if (r != null) {
- r.moveFocusableActivityToTop(reason);
- }
+ // Set focus to the top running activity of this task and move all its parents to top.
+ top.moveFocusableActivityToTop(reason);
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
if (noAnimation) {
- mDisplayContent.prepareAppTransition(TRANSIT_NONE, false /* alwaysKeepCurrent */);
- if (r != null) {
- mStackSupervisor.mNoAnimActivities.add(r);
- }
+ mDisplayContent.prepareAppTransitionOld(TRANSIT_OLD_NONE,
+ false /* alwaysKeepCurrent */);
+ mDisplayContent.prepareAppTransition(TRANSIT_NONE);
+ mStackSupervisor.mNoAnimActivities.add(top);
ActivityOptions.abort(options);
} else {
- updateTransitLocked(TRANSIT_TASK_TO_FRONT, options, false /* forceOverride */);
+ updateTransitLocked(TRANSIT_OLD_TASK_TO_FRONT, TRANSIT_TO_FRONT,
+ options, false /* forceOverride */);
}
// If a new task is moved to the front, then mark the existing top activity as
@@ -6880,7 +6970,9 @@
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task="
+ tr.mTaskId);
- mDisplayContent.prepareAppTransition(TRANSIT_TASK_TO_BACK, false /* alwaysKeepCurrent */);
+ mDisplayContent.prepareAppTransitionOld(TRANSIT_OLD_TASK_TO_BACK,
+ false /* alwaysKeepCurrent */);
+ mDisplayContent.prepareAppTransition(TRANSIT_TO_BACK);
moveToBack("moveTaskToBackLocked", tr);
if (inPinnedWindowingMode()) {
@@ -7145,7 +7237,7 @@
ActivityRecord source, ActivityOptions options) {
Task task;
- if (DisplayContent.alwaysCreateStack(getWindowingMode(), getActivityType())) {
+ if (DisplayContent.canReuseExistingTask(getWindowingMode(), getActivityType())) {
// This stack will only contain one task, so just return itself since all stacks ara now
// tasks and all tasks are now stacks.
task = reuseAsLeafTask(voiceSession, voiceInteractor, intent, info, activity);
@@ -7410,6 +7502,12 @@
outPos.y -= outset;
}
+ private Point getRelativePosition() {
+ Point position = new Point();
+ getRelativePosition(position);
+ return position;
+ }
+
boolean shouldIgnoreInput() {
if (inSplitScreenPrimaryWindowingMode() && !isFocusable()) {
return true;
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index c6b9e48..e721319 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -50,6 +50,7 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.os.IBinder;
import android.os.UserHandle;
import android.util.IntArray;
import android.util.Slog;
@@ -334,29 +335,6 @@
return true;
}
- void positionChildAt(int position, Task child, boolean includingParents,
- String updateLastFocusedTaskReason) {
- final Task prevFocusedTask = updateLastFocusedTaskReason != null ? getFocusedStack() : null;
-
- positionChildAt(position, child, includingParents);
-
- if (updateLastFocusedTaskReason == null) {
- return;
- }
-
- final Task currentFocusedStack = getFocusedStack();
- if (currentFocusedStack == prevFocusedTask) {
- return;
- }
-
- mLastFocusedStack = prevFocusedTask;
- EventLogTags.writeWmFocusedStack(mRootWindowContainer.mCurrentUser,
- mDisplayContent.mDisplayId,
- currentFocusedStack == null ? -1 : currentFocusedStack.getRootTaskId(),
- mLastFocusedStack == null ? -1 : mLastFocusedStack.getRootTaskId(),
- updateLastFocusedTaskReason);
- }
-
@Override
void positionChildAt(int position, Task child, boolean includingParents) {
final boolean moveToTop = position >= getChildCount() - 1;
@@ -751,7 +729,7 @@
// The split screen divider anchor is located above the split screen window.
mTmpLayerForSplitScreenDividerAnchor = layer++;
}
- if (s.isTaskAnimating() || s.isAppTransitioning()) {
+ if (s.isAnimatingByRecents() || s.isAppTransitioning()) {
// The animation layer is located above the highest animating stack and no
// higher.
mTmpLayerForAnimationLayer = layer++;
@@ -996,6 +974,13 @@
false /* createdByOrganizer */);
}
+ Task createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info,
+ Intent intent, boolean createdByOrganizer) {
+ return createStack(windowingMode, activityType, onTop, null /* info */, null /* intent */,
+ false /* createdByOrganizer */ , false /* deferTaskAppear */,
+ null /* launchCookie */);
+ }
+
/**
* Creates a stack matching the input windowing mode and activity type on this display.
*
@@ -1013,10 +998,14 @@
* @param intent The intent that started this task.
* @param createdByOrganizer @{code true} if this is created by task organizer, @{code false}
* otherwise.
+ * @param deferTaskAppear @{code true} if the task appeared signal should be deferred.
+ * @param launchCookie Launch cookie used for tracking/association of the task we are
+ * creating.
* @return The newly created stack.
*/
Task createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info,
- Intent intent, boolean createdByOrganizer) {
+ Intent intent, boolean createdByOrganizer, boolean deferTaskAppear,
+ IBinder launchCookie) {
if (activityType == ACTIVITY_TYPE_UNDEFINED && !createdByOrganizer) {
// Can't have an undefined stack type yet...so re-map to standard. Anyone that wants
// anything else should be passing it in anyways...except for the task organizer.
@@ -1048,7 +1037,7 @@
final int stackId = getNextStackId();
return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent,
- createdByOrganizer);
+ createdByOrganizer, deferTaskAppear, launchCookie);
}
/** @return the root task to create the next task in. */
@@ -1078,8 +1067,9 @@
}
@VisibleForTesting
- Task createStackUnchecked(int windowingMode, int activityType, int stackId,
- boolean onTop, ActivityInfo info, Intent intent, boolean createdByOrganizer) {
+ Task createStackUnchecked(int windowingMode, int activityType, int stackId, boolean onTop,
+ ActivityInfo info, Intent intent, boolean createdByOrganizer, boolean deferTaskAppear,
+ IBinder launchCookie) {
if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) {
throw new IllegalArgumentException("Stack with windowing mode cannot with non standard "
+ "activity type.");
@@ -1097,7 +1087,7 @@
}
final Task stack = new Task(mAtmService, stackId, activityType,
- info, intent, createdByOrganizer);
+ info, intent, createdByOrganizer, deferTaskAppear, launchCookie);
if (launchRootTask != null) {
launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
if (onTop) {
@@ -1189,6 +1179,24 @@
return mLastFocusedStack;
}
+ void updateLastFocusedRootTask(Task prevFocusedTask, String updateLastFocusedTaskReason) {
+ if (updateLastFocusedTaskReason == null) {
+ return;
+ }
+
+ final Task currentFocusedTask = getFocusedStack();
+ if (currentFocusedTask == prevFocusedTask) {
+ return;
+ }
+
+ mLastFocusedStack = prevFocusedTask;
+ EventLogTags.writeWmFocusedStack(mRootWindowContainer.mCurrentUser,
+ mDisplayContent.mDisplayId,
+ currentFocusedTask == null ? -1 : currentFocusedTask.getRootTaskId(),
+ mLastFocusedStack == null ? -1 : mLastFocusedStack.getRootTaskId(),
+ updateLastFocusedTaskReason);
+ }
+
boolean allResumedActivitiesComplete() {
for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityRecord r = getStackAt(stackNdx).getResumedActivity();
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 6486b78..a70efbc 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -53,6 +53,7 @@
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Objects;
import java.util.WeakHashMap;
import java.util.function.Consumer;
@@ -155,9 +156,8 @@
}
void onTaskInfoChanged(Task task, ActivityManager.RunningTaskInfo taskInfo) {
- if (!task.mCreatedByOrganizer && !task.mTaskAppearedSent) {
- // Skip if the task has not yet received taskAppeared(), except for tasks created
- // by the organizer that don't receive that signal
+ if (!task.mTaskAppearedSent) {
+ // Skip if the task has not yet received taskAppeared().
return;
}
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task info changed taskId=%d", task.mTaskId);
@@ -178,9 +178,8 @@
void onBackPressedOnTaskRoot(Task task) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task back pressed on root taskId=%d",
task.mTaskId);
- if (!task.mCreatedByOrganizer && !task.mTaskAppearedSent) {
- // Skip if the task has not yet received taskAppeared(), except for tasks created
- // by the organizer that don't receive that signal
+ if (!task.mTaskAppearedSent) {
+ // Skip if the task has not yet received taskAppeared().
return;
}
if (!task.isOrganized()) {
@@ -389,10 +388,6 @@
}
void onTaskAppeared(ITaskOrganizer organizer, Task task) {
- // Don't send onTaskAppeared signal for task created by organizer since we will return it in
- // the creation call.
- if (task.mCreatedByOrganizer) return;
-
final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
state.addTask(task);
}
@@ -405,34 +400,39 @@
}
@Override
- public TaskAppearedInfo createRootTask(int displayId, int windowingMode) {
+ public void createRootTask(int displayId, int windowingMode, @Nullable IBinder launchCookie) {
enforceStackPermission("createRootTask()");
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
DisplayContent display = mService.mRootWindowContainer.getDisplayContent(displayId);
if (display == null) {
- return null;
+ ProtoLog.e(WM_DEBUG_WINDOW_ORGANIZER,
+ "createRootTask unknown displayId=%d", displayId);
+ return;
}
- ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Create root task displayId=%d winMode=%d",
- displayId, windowingMode);
- final Task task = display.getDefaultTaskDisplayArea().createStack(windowingMode,
- ACTIVITY_TYPE_UNDEFINED, false /* onTop */, null /* info */, new Intent(),
- true /* createdByOrganizer */);
- RunningTaskInfo out = task.getTaskInfo();
- mLastSentTaskInfos.put(task, out);
- final TaskOrganizerState state =
- mTaskOrganizerStates.get(task.mTaskOrganizer.asBinder());
- final SurfaceControl outSurfaceControl = state.addTaskWithoutCallback(task,
- "TaskOrganizerController.createRootTask");
- return new TaskAppearedInfo(task.getTaskInfo(), outSurfaceControl);
+ createRootTask(display, windowingMode, launchCookie);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
+ @VisibleForTesting
+ Task createRootTask(DisplayContent display, int windowingMode, @Nullable IBinder launchCookie) {
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Create root task displayId=%d winMode=%d",
+ display.mDisplayId, windowingMode);
+ // We want to defer the task appear signal until the task is fully created and attached to
+ // to the hierarchy so that the complete starting configuration is in the task info we send
+ // over to the organizer.
+ final Task task = display.getDefaultTaskDisplayArea().createStack(windowingMode,
+ ACTIVITY_TYPE_UNDEFINED, false /* onTop */, null /* info */, new Intent(),
+ true /* createdByOrganizer */, true /* deferTaskAppear */, launchCookie);
+ task.setDeferTaskAppear(false /* deferTaskAppear */);
+ return task;
+ }
+
@Override
public boolean deleteRootTask(WindowContainerToken token) {
enforceStackPermission("deleteRootTask()");
@@ -483,6 +483,12 @@
boolean changed = lastInfo == null
|| mTmpTaskInfo.topActivityType != lastInfo.topActivityType
|| mTmpTaskInfo.isResizeable != lastInfo.isResizeable
+ || !Objects.equals(
+ mTmpTaskInfo.letterboxActivityBounds,
+ lastInfo.letterboxActivityBounds)
+ || !Objects.equals(
+ mTmpTaskInfo.positionInParent,
+ lastInfo.positionInParent)
|| mTmpTaskInfo.pictureInPictureParams != lastInfo.pictureInPictureParams
|| mTmpTaskInfo.getConfiguration().windowConfiguration.getWindowingMode()
!= lastInfo.getConfiguration().windowConfiguration.getWindowingMode()
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 01adb8b..3ce04af 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -157,7 +157,6 @@
if (shouldDisableSnapshots()) {
return;
}
-
// We need to take a snapshot of the task if and only if all activities of the task are
// either closing or hidden.
getClosingTasks(closingApps, mTmpTasks);
@@ -445,10 +444,17 @@
for (int i = closingApps.size() - 1; i >= 0; i--) {
final ActivityRecord activity = closingApps.valueAt(i);
final Task task = activity.getTask();
+ if (task == null) continue;
+ // Since RecentsAnimation will handle task snapshot while switching apps with the
+ // best capture timing (e.g. IME window capture),
+ // No need additional task capture while task is controlled by RecentsAnimation.
+ if (task.isAnimatingByRecents()) {
+ mSkipClosingAppSnapshotTasks.add(task);
+ }
// If the task of the app is not visible anymore, it means no other app in that task
// is opening. Thus, the task is closing.
- if (task != null && !task.isVisible() && !mSkipClosingAppSnapshotTasks.contains(task)) {
+ if (!task.isVisible() && !mSkipClosingAppSnapshotTasks.contains(task)) {
outClosingTasks.add(task);
}
}
@@ -571,7 +577,10 @@
synchronized (mService.mGlobalLock) {
mTmpTasks.clear();
mService.mRoot.forAllTasks(task -> {
- if (task.isVisible()) {
+ // Since RecentsAnimation will handle task snapshot while switching apps
+ // with the best capture timing (e.g. IME window capture), No need
+ // additional task capture while task is controlled by RecentsAnimation.
+ if (task.isVisible() && !task.isAnimatingByRecents()) {
mTmpTasks.add(task);
}
});
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index ac86698..d5322ea 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -22,8 +22,8 @@
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
import android.annotation.NonNull;
import android.os.Binder;
@@ -65,7 +65,7 @@
*/
private static final int STATE_PLAYING = 2;
- final @WindowManager.TransitionType int mType;
+ final @WindowManager.TransitionOldType int mType;
private int mSyncId;
private @WindowManager.TransitionFlags int mFlags;
private final TransitionController mController;
@@ -74,7 +74,7 @@
private int mState = STATE_COLLECTING;
private boolean mReadyCalled = false;
- Transition(@WindowManager.TransitionType int type,
+ Transition(@WindowManager.TransitionOldType int type,
@WindowManager.TransitionFlags int flags, TransitionController controller) {
mType = type;
mFlags = flags;
@@ -182,7 +182,7 @@
if (dc == null) {
return;
}
- if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
+ if (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY) {
if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
&& (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0
&& (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) == 0) {
@@ -196,10 +196,10 @@
}
}
}
- if (transit == TRANSIT_KEYGUARD_GOING_AWAY
- || transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER) {
+ if (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
+ || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER) {
dc.startKeyguardExitOnNonAppWindows(
- transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+ transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
(flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0,
(flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) != 0);
mController.mAtm.mWindowManager.mPolicy.startKeyguardExitAnimation(transit, 0);
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 7cdc177..c2fb4cd 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -16,13 +16,13 @@
package com.android.server.wm;
-import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
-import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
-import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -44,9 +44,9 @@
class TransitionController {
private static final String TAG = "TransitionController";
- private static final int[] SUPPORTED_LEGACY_TRANSIT_TYPES = {TRANSIT_TASK_OPEN,
- TRANSIT_TASK_CLOSE, TRANSIT_TASK_TO_FRONT, TRANSIT_TASK_TO_BACK,
- TRANSIT_TASK_OPEN_BEHIND, TRANSIT_KEYGUARD_GOING_AWAY};
+ private static final int[] SUPPORTED_LEGACY_TRANSIT_TYPES = {TRANSIT_OLD_TASK_OPEN,
+ TRANSIT_OLD_TASK_CLOSE, TRANSIT_OLD_TASK_TO_FRONT, TRANSIT_OLD_TASK_TO_BACK,
+ TRANSIT_OLD_TASK_OPEN_BEHIND, TRANSIT_OLD_KEYGUARD_GOING_AWAY};
static {
Arrays.sort(SUPPORTED_LEGACY_TRANSIT_TYPES);
}
@@ -79,7 +79,7 @@
* Creates a transition. It can immediately collect participants.
*/
@NonNull
- Transition createTransition(@WindowManager.TransitionType int type,
+ Transition createTransition(@WindowManager.TransitionOldType int type,
@WindowManager.TransitionFlags int flags) {
if (mCollectingTransition != null) {
throw new IllegalStateException("Simultaneous transitions not supported yet.");
@@ -137,13 +137,13 @@
* @return the created transition. Collection can start immediately.
*/
@NonNull
- Transition requestTransition(@WindowManager.TransitionType int type) {
+ Transition requestTransition(@WindowManager.TransitionOldType int type) {
return requestTransition(type, 0 /* flags */);
}
/** @see #requestTransition */
@NonNull
- Transition requestTransition(@WindowManager.TransitionType int type,
+ Transition requestTransition(@WindowManager.TransitionOldType int type,
@WindowManager.TransitionFlags int flags) {
if (mTransitionPlayer == null) {
throw new IllegalStateException("Shell Transitions not enabled");
@@ -169,7 +169,7 @@
*
* @return {@code true} if the transition is handled.
*/
- boolean adaptLegacyPrepare(@WindowManager.TransitionType int transit,
+ boolean adaptLegacyPrepare(@WindowManager.TransitionOldType int transit,
@WindowManager.TransitionFlags int flags, boolean forceOverride) {
if (!isShellTransitionsEnabled()
|| Arrays.binarySearch(SUPPORTED_LEGACY_TRANSIT_TYPES, transit) < 0) {
@@ -180,7 +180,7 @@
// TODO(shell-transitions): add to flags
} else if (forceOverride) {
// TODO(shell-transitions): sort out these flags
- } else if (transit == TRANSIT_CRASHING_ACTIVITY_CLOSE) {
+ } else if (transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE) {
// TODO(shell-transitions): record crashing
}
} else {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index da3a928..0f6b62b 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1144,7 +1144,7 @@
* @return {@code true} if handled; {@code false} otherwise.
*/
boolean onDescendantOrientationChanged(@Nullable IBinder freezeDisplayToken,
- @Nullable ConfigurationContainer requestingContainer) {
+ @Nullable WindowContainer requestingContainer) {
final WindowContainer parent = getParent();
if (parent == null) {
return false;
@@ -1156,7 +1156,7 @@
/**
* Check if this container or its parent will handle orientation changes from descendants. It's
* different from the return value of {@link #onDescendantOrientationChanged(IBinder,
- * ConfigurationContainer)} in the sense that the return value of this method tells if this
+ * WindowContainer)} in the sense that the return value of this method tells if this
* container or its parent will handle the request eventually, while the return value of the
* other method is if it handled the request synchronously.
*
@@ -1230,7 +1230,7 @@
* to ensure it gets correct configuration.
*/
void setOrientation(int orientation, @Nullable IBinder freezeDisplayToken,
- @Nullable ConfigurationContainer requestingContainer) {
+ @Nullable WindowContainer requestingContainer) {
if (mOrientation == orientation) {
return;
}
@@ -2550,7 +2550,8 @@
ProtoLog.d(WM_DEBUG_APP_TRANSITIONS,
"Loading animation for app transition. transit=%s enter=%b frame=%s insets=%s "
+ "surfaceInsets=%s",
- AppTransition.appTransitionToString(transit), enter, frame, insets, surfaceInsets);
+ AppTransition.appTransitionOldToString(transit), enter, frame, insets,
+ surfaceInsets);
final Configuration displayConfig = displayContent.getConfiguration();
final Animation a = getDisplayContent().mAppTransition.loadAnimation(lp, transit, enter,
displayConfig.uiMode, displayConfig.orientation, frame, displayFrame, insets,
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 66f2e4d..0eadbf2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -78,6 +78,9 @@
import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
+import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS;
import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
@@ -150,6 +153,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.TestUtilityService;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.database.ContentObserver;
@@ -254,7 +258,6 @@
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.RemoveContentMode;
-import android.view.WindowManager.TransitionType;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
import android.window.ClientWindowFrames;
@@ -419,6 +422,16 @@
DISABLE_TRIPLE_BUFFERING_PROPERTY, false);
/**
+ * Use new app transit framework.
+ */
+ private static final String USE_NEW_APP_TRANSIT =
+ "persist.wm.use_new_app_transit";
+ /**
+ * @see #USE_NEW_APP_TRANSIT
+ */
+ static boolean sUseNewAppTransit = SystemProperties.getBoolean(USE_NEW_APP_TRANSIT, false);
+
+ /**
* Allows a fullscreen windowing mode activity to launch in its desired orientation directly
* when the display has different orientation.
*/
@@ -552,6 +565,7 @@
final AppOpsManager mAppOps;
final PackageManagerInternal mPmInternal;
+ private final TestUtilityService mTestUtilityService;
final DisplayWindowSettings mDisplayWindowSettings;
@@ -1253,6 +1267,7 @@
mAppOps.startWatchingMode(AppOpsManager.OP_TOAST_WINDOW, null, opListener);
mPmInternal = LocalServices.getService(PackageManagerInternal.class);
+ mTestUtilityService = LocalServices.getService(TestUtilityService.class);
final IntentFilter suspendPackagesFilter = new IntentFilter();
suspendPackagesFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
suspendPackagesFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
@@ -1860,8 +1875,9 @@
// animation and piggy-back on existing transition animation infrastructure.
final DisplayContent dc = activity.getDisplayContent();
dc.mOpeningApps.add(activity);
- dc.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_RELAUNCH, ALWAYS_KEEP_CURRENT,
- 0 /* flags */, false /* forceOverride */);
+ dc.prepareAppTransitionOld(WindowManager.TRANSIT_OLD_ACTIVITY_RELAUNCH,
+ ALWAYS_KEEP_CURRENT, 0 /* flags */, false /* forceOverride */);
+ dc.prepareAppTransition(TRANSIT_RELAUNCH);
dc.mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top,
frame.width(), frame.height());
dc.executeAppTransition();
@@ -1876,8 +1892,9 @@
final DisplayContent dc = activity.getDisplayContent();
if (mDisplayFrozen && !dc.mOpeningApps.contains(activity) && activity.isRelaunching()) {
dc.mOpeningApps.add(activity);
- dc.prepareAppTransition(WindowManager.TRANSIT_NONE, !ALWAYS_KEEP_CURRENT, 0 /* flags */,
+ dc.prepareAppTransitionOld(TRANSIT_OLD_NONE, !ALWAYS_KEEP_CURRENT, 0 /* flags */,
false /* forceOverride */);
+ dc.prepareAppTransition(TRANSIT_NONE);
dc.executeAppTransition();
}
}
@@ -2757,14 +2774,14 @@
}
// TODO(multi-display): remove when no default display use case.
- // (i.e. KeyguardController / RecentsAnimation)
- @Override
- public void prepareAppTransition(@TransitionType int transit, boolean alwaysKeepCurrent) {
+ void prepareAppTransitionNone() {
if (!checkCallingPermission(MANAGE_APP_TOKENS, "prepareAppTransition()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
- getDefaultDisplayContentLocked().prepareAppTransition(transit,
- alwaysKeepCurrent, 0 /* flags */, false /* forceOverride */);
+ getDefaultDisplayContentLocked().prepareAppTransitionOld(TRANSIT_OLD_NONE,
+ false /* alwaysKeepCurrent */,
+ 0 /* flags */, false /* forceOverride */);
+ getDefaultDisplayContentLocked().prepareAppTransition(TRANSIT_NONE);
}
@Override
@@ -2810,7 +2827,6 @@
// TODO(multi-display): remove when no default display use case.
// (i.e. KeyguardController / RecentsAnimation)
- @Override
public void executeAppTransition() {
if (!checkCallingPermission(MANAGE_APP_TOKENS, "executeAppTransition()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -6912,9 +6928,6 @@
if (!checkCallingPermission(READ_FRAME_BUFFER, "requestScrollCapture()")) {
throw new SecurityException("Requires READ_FRAME_BUFFER permission");
}
- if (behindClient != null && !isWindowToken(behindClient)) {
- throw new IllegalArgumentException("behindClient must be a window token");
- }
final long token = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -8339,9 +8352,8 @@
}
@Override
- public void holdLock(int durationMs) {
- mContext.enforceCallingPermission(
- Manifest.permission.INJECT_EVENTS, "holdLock requires shell identity");
+ public void holdLock(IBinder token, int durationMs) {
+ mTestUtilityService.verifyHoldLockToken(token);
synchronized (mGlobalLock) {
SystemClock.sleep(durationMs);
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 3011f25..fe312e6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -111,6 +111,8 @@
return runGetIgnoreOrientationRequest(pw);
case "dump-visible-window-views":
return runDumpVisibleWindowViews(pw);
+ case "reset":
+ return runReset(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -507,6 +509,34 @@
return 0;
}
+ private int runReset(PrintWriter pw) throws RemoteException {
+ int displayId = getDisplayId(getNextArg());
+
+ // size
+ mInterface.clearForcedDisplaySize(displayId);
+
+ // density
+ mInterface.clearForcedDisplayDensityForUser(displayId, UserHandle.USER_CURRENT);
+
+ // folded-area
+ mInternal.setOverrideFoldedArea(new Rect());
+
+ // scaling
+ mInterface.setForcedDisplayScalingMode(displayId, DisplayContent.FORCE_SCALING_MODE_AUTO);
+
+ // user-rotation
+ mInternal.thawDisplayRotation(displayId);
+
+ // fixed-to-user-rotation
+ mInterface.setFixedToUserRotation(displayId, IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT);
+
+ // set-ignore-orientation-request
+ mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */);
+
+ pw.println("Reset all settings for displayId=" + displayId);
+ return 0;
+ }
+
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
@@ -533,6 +563,8 @@
pw.println(" set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]");
pw.println(" get-ignore-orientation-request [-d DISPLAY_ID] ");
pw.println(" If app requested orientation should be ignored.");
+ pw.println(" reset [-d DISPLAY_ID]");
+ pw.println(" Reset all override settings.");
if (!IS_USER) {
pw.println(" tracing (start | stop)");
pw.println(" Start or stop window tracing.");
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 2e7905c..2d69dcb 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -505,29 +505,33 @@
}
}
- boolean areBackgroundActivityStartsAllowed() {
- // allow if any activity in the caller has either started or finished very recently, and
- // it must be started or finished after last stop app switches time.
- final long now = SystemClock.uptimeMillis();
- if (now - mLastActivityLaunchTime < ACTIVITY_BG_START_GRACE_PERIOD_MS
- || now - mLastActivityFinishTime < ACTIVITY_BG_START_GRACE_PERIOD_MS) {
- // if activity is started and finished before stop app switch time, we should not
- // let app to be able to start background activity even it's in grace period.
- if (mLastActivityLaunchTime > mAtm.getLastStopAppSwitchesTime()
- || mLastActivityFinishTime > mAtm.getLastStopAppSwitchesTime()) {
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "[WindowProcessController(" + mPid
- + ")] Activity start allowed: within "
- + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period");
+ boolean areBackgroundActivityStartsAllowed(boolean appSwitchAllowed) {
+ // If app switching is not allowed, we ignore all the start activity grace period
+ // exception so apps cannot start itself in onPause() after pressing home button.
+ if (appSwitchAllowed) {
+ // allow if any activity in the caller has either started or finished very recently, and
+ // it must be started or finished after last stop app switches time.
+ final long now = SystemClock.uptimeMillis();
+ if (now - mLastActivityLaunchTime < ACTIVITY_BG_START_GRACE_PERIOD_MS
+ || now - mLastActivityFinishTime < ACTIVITY_BG_START_GRACE_PERIOD_MS) {
+ // if activity is started and finished before stop app switch time, we should not
+ // let app to be able to start background activity even it's in grace period.
+ if (mLastActivityLaunchTime > mAtm.getLastStopAppSwitchesTime()
+ || mLastActivityFinishTime > mAtm.getLastStopAppSwitchesTime()) {
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(TAG, "[WindowProcessController(" + mPid
+ + ")] Activity start allowed: within "
+ + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period");
+ }
+ return true;
}
- return true;
- }
- if (DEBUG_ACTIVITY_STARTS) {
- Slog.d(TAG, "[WindowProcessController(" + mPid + ")] Activity start within "
- + ACTIVITY_BG_START_GRACE_PERIOD_MS
- + "ms grace period but also within stop app switch window");
- }
+ if (DEBUG_ACTIVITY_STARTS) {
+ Slog.d(TAG, "[WindowProcessController(" + mPid + ")] Activity start within "
+ + ACTIVITY_BG_START_GRACE_PERIOD_MS
+ + "ms grace period but also within stop app switch window");
+ }
+ }
}
// allow if the proc is instrumenting with background activity starts privs
if (mInstrumentingWithBackgroundActivityStartPrivileges) {
@@ -539,7 +543,7 @@
return true;
}
// allow if the caller has an activity in any foreground task
- if (hasActivityInVisibleTask()) {
+ if (appSwitchAllowed && hasActivityInVisibleTask()) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[WindowProcessController(" + mPid
+ ")] Activity start allowed: process has activity in foreground task");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a4b4fcb..3f15ff8 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1745,8 +1745,8 @@
* a combination of the above "visible now" with the check that the
* Input Manager uses when discarding windows from input consideration.
*/
- boolean isPotentialDragTarget() {
- return isVisibleNow() && !mRemoved
+ boolean isPotentialDragTarget(boolean targetInterceptsGlobalDrag) {
+ return (targetInterceptsGlobalDrag || isVisibleNow()) && !mRemoved
&& mInputChannel != null && mInputWindowHandle != null;
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 0111d48..8713645 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -27,7 +27,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DRAW;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
@@ -1075,7 +1075,7 @@
}
if (attr >= 0) {
a = mWin.getDisplayContent().mAppTransition.loadAnimationAttr(
- mWin.mAttrs, attr, TRANSIT_NONE);
+ mWin.mAttrs, attr, TRANSIT_OLD_NONE);
}
}
if (DEBUG_ANIM) Slog.v(TAG,
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index d6a56ba..9f83baf 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -38,7 +38,6 @@
"com_android_server_locksettings_SyntheticPasswordManager.cpp",
"com_android_server_net_NetworkStatsService.cpp",
"com_android_server_power_PowerManagerService.cpp",
- "com_android_server_powerstats_PowerStatsService.cpp",
"com_android_server_security_VerityUtils.cpp",
"com_android_server_SerialService.cpp",
"com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp",
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index d39c7a6..5d78f12 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -17,7 +17,6 @@
#define LOG_TAG "BatteryStatsService"
//#define LOG_NDEBUG 0
-#include <climits>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -28,6 +27,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <climits>
#include <unordered_map>
#include <utility>
@@ -66,10 +66,9 @@
namespace android
{
-#define LAST_RESUME_REASON "/sys/kernel/wakeup_reasons/last_resume_reason"
-#define MAX_REASON_SIZE 512
-
static bool wakeup_init = false;
+static std::mutex mReasonsMutex;
+static std::vector<std::string> mWakeupReasons;
static sem_t wakeup_sem;
extern sp<ISuspendControlService> getSuspendControl();
@@ -115,9 +114,25 @@
sp<PowerHalDeathRecipient> gDeathRecipient = new PowerHalDeathRecipient();
class WakeupCallback : public BnSuspendCallback {
- public:
- binder::Status notifyWakeup(bool success) override {
+public:
+ binder::Status notifyWakeup(bool success,
+ const std::vector<std::string>& wakeupReasons) override {
ALOGI("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
+ bool reasonsCaptured = false;
+ {
+ std::unique_lock<std::mutex> reasonsLock(mReasonsMutex, std::defer_lock);
+ if (reasonsLock.try_lock() && mWakeupReasons.empty()) {
+ mWakeupReasons = std::move(wakeupReasons);
+ reasonsCaptured = true;
+ }
+ }
+ if (!reasonsCaptured) {
+ ALOGE("Failed to write wakeup reasons. Reasons dropped:");
+ for (auto wakeupReason : wakeupReasons) {
+ ALOGE("\t%s", wakeupReason.c_str());
+ }
+ }
+
int ret = sem_post(&wakeup_sem);
if (ret < 0) {
char buf[80];
@@ -157,8 +172,6 @@
// Wait for wakeup.
ALOGV("Waiting for wakeup...");
- // TODO(b/116747600): device can suspend and wakeup after sem_wait() finishes and before wakeup
- // reason is recorded, i.e. BatteryStats might occasionally miss wakeup events.
int ret = sem_wait(&wakeup_sem);
if (ret < 0) {
char buf[80];
@@ -168,20 +181,27 @@
return 0;
}
- FILE *fp = fopen(LAST_RESUME_REASON, "r");
- if (fp == NULL) {
- ALOGE("Failed to open %s", LAST_RESUME_REASON);
- return -1;
- }
-
char* mergedreason = (char*)env->GetDirectBufferAddress(outBuf);
int remainreasonlen = (int)env->GetDirectBufferCapacity(outBuf);
ALOGV("Reading wakeup reasons");
+ std::vector<std::string> wakeupReasons;
+ {
+ std::unique_lock<std::mutex> reasonsLock(mReasonsMutex, std::defer_lock);
+ if (reasonsLock.try_lock() && !mWakeupReasons.empty()) {
+ wakeupReasons = std::move(mWakeupReasons);
+ mWakeupReasons.clear();
+ }
+ }
+
+ if (wakeupReasons.empty()) {
+ return 0;
+ }
+
char* mergedreasonpos = mergedreason;
- char reasonline[128];
int i = 0;
- while (fgets(reasonline, sizeof(reasonline), fp) != NULL) {
+ for (auto wakeupReason : wakeupReasons) {
+ auto reasonline = const_cast<char*>(wakeupReason.c_str());
char* pos = reasonline;
char* endPos;
int len;
@@ -238,10 +258,6 @@
*mergedreasonpos = 0;
}
- if (fclose(fp) != 0) {
- ALOGE("Failed to close %s", LAST_RESUME_REASON);
- return -1;
- }
return mergedreasonpos - mergedreason;
}
diff --git a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp
deleted file mode 100644
index 5eb6b73..0000000
--- a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * 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 "PowerStatsService"
-
-#include <android/hardware/power/stats/1.0/IPowerStats.h>
-#include <jni.h>
-#include <nativehelper/JNIHelp.h>
-
-#include <log/log.h>
-
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::hardware::power::stats::V1_0::EnergyData;
-using android::hardware::power::stats::V1_0::RailInfo;
-using android::hardware::power::stats::V1_0::Status;
-
-static jclass class_railInfo;
-static jmethodID method_railInfoInit;
-static jclass class_energyData;
-static jmethodID method_energyDataInit;
-
-namespace android {
-
-static std::mutex gPowerStatsHalMutex;
-static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0_ptr = nullptr;
-
-static void deinitPowerStats() {
- gPowerStatsHalV1_0_ptr = nullptr;
-}
-
-struct PowerStatsHalDeathRecipient : virtual public hardware::hidl_death_recipient {
- virtual void serviceDied(uint64_t cookie,
- const wp<android::hidl::base::V1_0::IBase> &who) override {
- // The HAL just died. Reset all handles to HAL services.
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
- deinitPowerStats();
- }
-};
-
-sp<PowerStatsHalDeathRecipient> gPowerStatsHalDeathRecipient = new PowerStatsHalDeathRecipient();
-
-static bool connectToPowerStatsHal() {
- if (gPowerStatsHalV1_0_ptr == nullptr) {
- gPowerStatsHalV1_0_ptr = android::hardware::power::stats::V1_0::IPowerStats::getService();
-
- if (gPowerStatsHalV1_0_ptr == nullptr) {
- ALOGE("Unable to get power.stats HAL service.");
- return false;
- }
-
- // Link death recipient to power.stats service handle
- hardware::Return<bool> linked =
- gPowerStatsHalV1_0_ptr->linkToDeath(gPowerStatsHalDeathRecipient, 0);
- if (!linked.isOk()) {
- ALOGE("Transaction error in linking to power.stats HAL death: %s",
- linked.description().c_str());
- deinitPowerStats();
- return false;
- } else if (!linked) {
- ALOGW("Unable to link to power.stats HAL death notifications");
- return false;
- }
- }
- return true;
-}
-
-static bool checkResult(const Return<void> &ret, const char *function) {
- if (!ret.isOk()) {
- ALOGE("%s failed: requested HAL service not available. Description: %s", function,
- ret.description().c_str());
- if (ret.isDeadObject()) {
- deinitPowerStats();
- }
- return false;
- }
- return true;
-}
-
-static jobjectArray nativeGetRailInfo(JNIEnv *env, jclass clazz) {
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
- if (!connectToPowerStatsHal()) {
- ALOGE("nativeGetRailInfo failed to connect to power.stats HAL");
- return nullptr;
- }
-
- hidl_vec<RailInfo> list;
- Return<void> ret = gPowerStatsHalV1_0_ptr->getRailInfo([&list](auto rails, auto status) {
- if (status != Status::SUCCESS) {
- ALOGW("Rail information is not available");
- } else {
- list = std::move(rails);
- }
- });
-
- if (!checkResult(ret, __func__)) {
- ALOGE("getRailInfo failed");
- return nullptr;
- } else {
- jobjectArray railInfoArray = env->NewObjectArray(list.size(), class_railInfo, nullptr);
- for (int i = 0; i < list.size(); i++) {
- jstring railName = env->NewStringUTF(list[i].railName.c_str());
- jstring subsysName = env->NewStringUTF(list[i].subsysName.c_str());
- jobject railInfo = env->NewObject(class_railInfo, method_railInfoInit, list[i].index,
- railName, subsysName, list[i].samplingRate);
- env->SetObjectArrayElement(railInfoArray, i, railInfo);
- env->DeleteLocalRef(railName);
- env->DeleteLocalRef(subsysName);
- env->DeleteLocalRef(railInfo);
- }
- return railInfoArray;
- }
-}
-
-static jobjectArray nativeGetEnergyData(JNIEnv *env, jclass clazz) {
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
- if (!connectToPowerStatsHal()) {
- ALOGE("nativeGetEnergy failed to connect to power.stats HAL");
- }
-
- hidl_vec<EnergyData> list;
- Return<void> ret =
- gPowerStatsHalV1_0_ptr->getEnergyData({}, [&list](auto energyData, auto status) {
- if (status != Status::SUCCESS) {
- ALOGW("getEnergyData is not supported");
- } else {
- list = std::move(energyData);
- }
- });
-
- if (!checkResult(ret, __func__)) {
- ALOGE("getEnergyData failed");
- return nullptr;
- } else {
- jobjectArray energyDataArray = env->NewObjectArray(list.size(), class_energyData, nullptr);
- for (int i = 0; i < list.size(); i++) {
- jobject energyData = env->NewObject(class_energyData, method_energyDataInit,
- list[i].index, list[i].timestamp, list[i].energy);
- env->SetObjectArrayElement(energyDataArray, i, energyData);
- env->DeleteLocalRef(energyData);
- }
- return energyDataArray;
- }
-}
-
-static jboolean nativeInit(JNIEnv *env, jclass clazz) {
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
- jclass temp = env->FindClass("com/android/server/powerstats/PowerStatsData$RailInfo");
- if (temp == nullptr) return false;
-
- class_railInfo = (jclass)env->NewGlobalRef(temp);
- if (class_railInfo == nullptr) return false;
-
- method_railInfoInit =
- env->GetMethodID(class_railInfo, "<init>", "(JLjava/lang/String;Ljava/lang/String;J)V");
- if (method_railInfoInit == nullptr) return false;
-
- temp = env->FindClass("com/android/server/powerstats/PowerStatsData$EnergyData");
- if (temp == nullptr) return false;
-
- class_energyData = (jclass)env->NewGlobalRef(temp);
- if (class_energyData == nullptr) return false;
-
- method_energyDataInit = env->GetMethodID(class_energyData, "<init>", "(JJJ)V");
- if (method_energyDataInit == nullptr) return false;
-
- bool rv = true;
-
- if (!connectToPowerStatsHal()) {
- ALOGE("nativeInit failed to connect to power.stats HAL");
- rv = false;
- } else {
- Return<void> ret = gPowerStatsHalV1_0_ptr->getRailInfo([&rv](auto rails, auto status) {
- if (status != Status::SUCCESS) {
- ALOGE("nativeInit RailInfo is unavailable");
- rv = false;
- }
- });
-
- ret = gPowerStatsHalV1_0_ptr->getEnergyData({}, [&rv](auto energyData, auto status) {
- if (status != Status::SUCCESS) {
- ALOGE("nativeInit EnergyData is unavailable");
- rv = false;
- }
- });
- }
-
- return rv;
-}
-
-static const JNINativeMethod method_table[] = {
- {"nativeInit", "()Z", (void *)nativeInit},
- {"nativeGetRailInfo", "()[Lcom/android/server/powerstats/PowerStatsData$RailInfo;",
- (void *)nativeGetRailInfo},
- {"nativeGetEnergyData", "()[Lcom/android/server/powerstats/PowerStatsData$EnergyData;",
- (void *)nativeGetEnergyData},
-};
-
-int register_android_server_PowerStatsService(JNIEnv *env) {
- return jniRegisterNativeMethods(env,
- "com/android/server/powerstats/"
- "PowerStatsHALWrapper$PowerStatsHALWrapperImpl",
- method_table, NELEM(method_table));
-}
-
-}; // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 48d5244..0ffa5c3 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -29,7 +29,6 @@
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
-int register_android_server_PowerStatsService(JNIEnv* env);
int register_android_server_storage_AppFuse(JNIEnv* env);
int register_android_server_SerialService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
@@ -84,7 +83,6 @@
register_android_server_broadcastradio_BroadcastRadioService(env);
register_android_server_broadcastradio_Tuner(vm, env);
register_android_server_PowerManagerService(env);
- register_android_server_PowerStatsService(env);
register_android_server_SerialService(env);
register_android_server_InputManager(env);
register_android_server_LightsService(env);
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index b7d6424..fb55e75 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -20,3 +20,11 @@
api_dir: "display-device-config/schema",
package_name: "com.android.server.display.config",
}
+
+
+xsd_config {
+ name: "cec-config",
+ srcs: ["cec-config/cec-config.xsd"],
+ api_dir: "cec-config/schema",
+ package_name: "com.android.server.hdmi.cec.config",
+}
diff --git a/services/core/xsd/cec-config/cec-config.xsd b/services/core/xsd/cec-config/cec-config.xsd
new file mode 100644
index 0000000..0801c88
--- /dev/null
+++ b/services/core/xsd/cec-config/cec-config.xsd
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema version="2.0"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <xs:element name="cec-settings">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="setting" type="setting" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="setting">
+ <xs:attribute name="name" type="xs:string"/>
+ <xs:attribute name="user-configurable" type="xs:boolean"/>
+ <xs:element name="allowed-values" type="value-list" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="default-value" type="value" minOccurs="1" maxOccurs="1"/>
+ </xs:complexType>
+ <xs:complexType name="value-list">
+ <xs:sequence>
+ <xs:element name="value" type="value" minOccurs="1" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="value">
+ <xs:attribute name="string-value" type="xs:string"/>
+ </xs:complexType>
+</xs:schema>
diff --git a/services/core/xsd/cec-config/schema/current.txt b/services/core/xsd/cec-config/schema/current.txt
new file mode 100644
index 0000000..34faf45
--- /dev/null
+++ b/services/core/xsd/cec-config/schema/current.txt
@@ -0,0 +1,40 @@
+// Signature format: 2.0
+package com.android.server.hdmi.cec.config {
+
+ public class CecSettings {
+ ctor public CecSettings();
+ method public java.util.List<com.android.server.hdmi.cec.config.Setting> getSetting();
+ }
+
+ public class Setting {
+ ctor public Setting();
+ method public com.android.server.hdmi.cec.config.ValueList getAllowedValues();
+ method public com.android.server.hdmi.cec.config.Value getDefaultValue();
+ method public String getName();
+ method public boolean getUserConfigurable();
+ method public void setAllowedValues(com.android.server.hdmi.cec.config.ValueList);
+ method public void setDefaultValue(com.android.server.hdmi.cec.config.Value);
+ method public void setName(String);
+ method public void setUserConfigurable(boolean);
+ }
+
+ public class Value {
+ ctor public Value();
+ method public String getStringValue();
+ method public void setStringValue(String);
+ }
+
+ public class ValueList {
+ ctor public ValueList();
+ method public java.util.List<com.android.server.hdmi.cec.config.Value> getValue();
+ }
+
+ public class XmlParser {
+ ctor public XmlParser();
+ method public static com.android.server.hdmi.cec.config.CecSettings read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ }
+
+}
+
diff --git a/services/core/xsd/cec-config/schema/last_current.txt b/services/core/xsd/cec-config/schema/last_current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/core/xsd/cec-config/schema/last_current.txt
diff --git a/services/core/xsd/cec-config/schema/last_removed.txt b/services/core/xsd/cec-config/schema/last_removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/core/xsd/cec-config/schema/last_removed.txt
diff --git a/services/core/xsd/cec-config/schema/removed.txt b/services/core/xsd/cec-config/schema/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/core/xsd/cec-config/schema/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/coverage/Android.bp b/services/coverage/Android.bp
index df054b0..b3cee37 100644
--- a/services/coverage/Android.bp
+++ b/services/coverage/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.coverage",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [":services.coverage-sources"],
libs: ["jacocoagent"],
}
diff --git a/services/devicepolicy/Android.bp b/services/devicepolicy/Android.bp
index 7a80fb1..5de48ae 100644
--- a/services/devicepolicy/Android.bp
+++ b/services/devicepolicy/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.devicepolicy",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [":services.devicepolicy-sources"],
libs: [
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 745d645..411d8d6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1231,12 +1231,14 @@
return "/data/system/";
}
+ @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
PendingIntent pendingIntentGetActivityAsUser(Context context, int requestCode,
@NonNull Intent intent, int flags, Bundle options, UserHandle user) {
return PendingIntent.getActivityAsUser(
context, requestCode, intent, flags, options, user);
}
+ @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
PendingIntent pendingIntentGetBroadcast(
Context context, int requestCode, Intent intent, int flags) {
return PendingIntent.getBroadcast(context, requestCode, intent, flags);
@@ -1647,6 +1649,29 @@
}
/**
+ * Creates a new {@link CallerIdentity} object to represent the caller's identity.
+ * If an {@code adminComponent} is specified, then the caller must be an admin and
+ * the provided component name must match the caller's UID.
+ *
+ * If a package name is provided, then the caller doesn't have to be an admin, and the
+ * provided package must belong to the caller's UID.
+ *
+ * If neither is provided, the caller identity is returned as-is.
+ *
+ * Note: this method should only be called when the caller may not be an admin. If the caller
+ * is not an admin, the ComponentName in the returned identity will be null.
+ */
+ private CallerIdentity getNonPrivilegedOrAdminCallerIdentity(
+ @Nullable ComponentName adminComponent,
+ @Nullable String callerPackage) {
+ if (adminComponent != null) {
+ return getCallerIdentity(adminComponent);
+ }
+
+ return getNonPrivilegedOrAdminCallerIdentityUsingPackage(callerPackage);
+ }
+
+ /**
* Retrieves the active admin of the caller. This method should not be called directly and
* should only be called by {@link #getAdminCallerIdentity},
* {@link #getNonPrivilegedOrAdminCallerIdentity}, {@link #getAdminCallerIdentityUsingPackage}
@@ -2215,7 +2240,8 @@
caller.getUid(), doAdmin.getUid());
Preconditions.checkCallAuthorization(
- doAdmin.info.getComponent().equals(caller.getComponentName()),
+ !caller.hasAdminComponent()
+ || doAdmin.info.getComponent().equals(caller.getComponentName()),
"Caller component %s is not device owner",
caller.getComponentName());
@@ -2238,7 +2264,8 @@
caller.getUid());
Preconditions.checkCallAuthorization(
- poAdmin.info.getComponent().equals(caller.getComponentName()),
+ !caller.hasAdminComponent()
+ || poAdmin.info.getComponent().equals(caller.getComponentName()),
"Caller component %s is not profile owner",
caller.getComponentName());
@@ -2311,14 +2338,6 @@
final boolean isDeviceOwner = isDeviceOwner(admin.info.getComponent(), userId);
final boolean isProfileOwner = isProfileOwner(admin.info.getComponent(), userId);
- if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
- throw new SecurityException("Admin " + admin.info.getComponent()
- + " does not own the device");
- }
- if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) {
- throw new SecurityException("Admin " + admin.info.getComponent()
- + " does not own the profile");
- }
if (DA_DISALLOWED_POLICIES.contains(reqPolicy) && !isDeviceOwner && !isProfileOwner) {
throw new SecurityException("Admin " + admin.info.getComponent()
+ " is not a device owner or profile owner, so may not use policy: "
@@ -2424,20 +2443,11 @@
ensureLocked();
final boolean ownsDevice = isDeviceOwner(admin.info.getComponent(), userId);
final boolean ownsProfile = isProfileOwner(admin.info.getComponent(), userId);
- final boolean ownsProfileOnOrganizationOwnedDevice =
- isProfileOwnerOfOrganizationOwnedDevice(admin.info.getComponent(), userId);
- if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
- return ownsDevice;
- } else if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) {
- // DO always has the PO power.
- return ownsDevice || ownsProfileOnOrganizationOwnedDevice || ownsProfile;
- } else {
- boolean allowedToUsePolicy = ownsDevice || ownsProfile
- || !DA_DISALLOWED_POLICIES.contains(reqPolicy)
- || getTargetSdk(admin.info.getPackageName(), userId) < Build.VERSION_CODES.Q;
- return allowedToUsePolicy && admin.info.usesPolicy(reqPolicy);
- }
+ boolean allowedToUsePolicy = ownsDevice || ownsProfile
+ || !DA_DISALLOWED_POLICIES.contains(reqPolicy)
+ || getTargetSdk(admin.info.getPackageName(), userId) < Build.VERSION_CODES.Q;
+ return allowedToUsePolicy && admin.info.usesPolicy(reqPolicy);
}
void sendAdminCommandLocked(ActiveAdmin admin, String action) {
@@ -4423,23 +4433,24 @@
}
// If caller has PO (or DO) throw or fail silently depending on its target SDK level.
- Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isProfileOwner(caller),
- String.format("UID %d is not a device or profile owner", caller.getUid()));
-
- synchronized (getLockObject()) {
- ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userHandle);
- if (admin != null) {
+ if (isDeviceOwner(caller) || isProfileOwner(caller)) {
+ synchronized (getLockObject()) {
+ ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
if (getTargetSdk(admin.info.getPackageName(), userHandle) < Build.VERSION_CODES.O) {
Slog.e(LOG_TAG, "DPC can no longer call resetPassword()");
return false;
}
throw new SecurityException("Device admin can no longer call resetPassword()");
}
+ }
+ // Caller is not DO or PO, could either be unauthorized or Device Admin.
+ synchronized (getLockObject()) {
// Legacy device admin cannot call resetPassword either
- admin = getActiveAdminForCallerLocked(
+ ActiveAdmin admin = getActiveAdminForCallerLocked(
null, DeviceAdminInfo.USES_POLICY_RESET_PASSWORD, false);
+ Preconditions.checkCallAuthorization(admin != null,
+ "Unauthorized caller cannot call resetPassword.");
if (getTargetSdk(admin.info.getPackageName(),
userHandle) <= android.os.Build.VERSION_CODES.M) {
Slog.e(LOG_TAG, "Device admin can no longer call resetPassword()");
@@ -5483,22 +5494,17 @@
public List<String> getDelegatedScopes(ComponentName who,
String delegatePackage) throws SecurityException {
Objects.requireNonNull(delegatePackage, "Delegate package is null");
+ final CallerIdentity caller = getNonPrivilegedOrAdminCallerIdentity(who, delegatePackage);
- // Retrieve the user ID of the calling process.
- final int callingUid = mInjector.binderGetCallingUid();
- final int userId = UserHandle.getUserId(callingUid);
+ // Ensure the caller may call this method:
+ // * Either it's an admin
+ // * Or it's an app identified by its calling package name (the
+ // getNonPrivilegedOrAdminCallerIdentity method validated the UID and package match).
+ Preconditions.checkCallAuthorization(
+ (caller.hasAdminComponent() && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || delegatePackage != null);
synchronized (getLockObject()) {
- // Ensure calling process is device/profile owner.
- if (who != null) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- // Or ensure calling process is delegatePackage itself.
- } else {
- if (!isCallingFromPackage(delegatePackage, callingUid)) {
- throw new SecurityException("Caller with uid " + callingUid + " is not "
- + delegatePackage);
- }
- }
- final DevicePolicyData policy = getUserData(userId);
+ final DevicePolicyData policy = getUserData(caller.getUserId());
// Retrieve the scopes assigned to delegatePackage, or null if no scope was given.
final List<String> scopes = policy.mDelegationMap.get(delegatePackage);
return scopes == null ? Collections.EMPTY_LIST : scopes;
@@ -5788,8 +5794,9 @@
public boolean isAlwaysOnVpnLockdownEnabled(ComponentName admin) throws SecurityException {
Objects.requireNonNull(admin, "ComponentName is null");
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
+ final CallerIdentity caller = getNonPrivilegedOrAdminCallerIdentity(admin);
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isDeviceOwner(caller) || isProfileOwner(caller)))
|| hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK));
return mInjector.binderWithCleanCallingIdentity(
@@ -7887,7 +7894,7 @@
// Current user has a managed-profile, but current user is not managed, so
// rather than moving to finalized state, go back to unmanaged once
// profile provisioning is complete.
- if (newState == DevicePolicyManager.STATE_USER_PROFILE_FINALIZED) {
+ if (newState == DevicePolicyManager.STATE_USER_UNMANAGED) {
return;
}
break;
@@ -8356,37 +8363,27 @@
}
private void enforceDeviceOwnerOrManageUsers() {
- synchronized (getLockObject()) {
- if (getActiveAdminWithPolicyForUidLocked(null, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER,
- mInjector.binderGetCallingUid()) != null) {
- return;
- }
+ final CallerIdentity caller = getCallerIdentity();
+ if (isDeviceOwner(caller)) {
+ return;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+ Preconditions.checkCallAuthorization(canManageUsers(caller));
}
private void enforceProfileOwnerOrSystemUser() {
- synchronized (getLockObject()) {
- if (getActiveAdminWithPolicyForUidLocked(null,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, mInjector.binderGetCallingUid())
- != null) {
- return;
- }
+ final CallerIdentity caller = getCallerIdentity();
+ if (isDeviceOwner(caller) || isProfileOwner(caller)) {
+ return;
}
- Preconditions.checkState(isCallerWithSystemUid(),
+ Preconditions.checkState(isSystemUid(caller),
"Only profile owner, device owner and system may call this method.");
}
private void enforceProfileOwnerOrFullCrossUsersPermission(CallerIdentity caller,
int userId) {
- if (userId == caller.getUserId()) {
- synchronized (getLockObject()) {
- if (getActiveAdminWithPolicyForUidLocked(null,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, caller.getUid()) != null) {
- // Device Owner/Profile Owner may access the user it runs on.
- return;
- }
- }
+ if ((userId == caller.getUserId()) && (isProfileOwner(caller) || isDeviceOwner(caller))) {
+ // Device Owner/Profile Owner may access the user it runs on.
+ return;
}
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
}
@@ -9279,10 +9276,11 @@
throw new IllegalArgumentException("profileOwner " + profileOwner + " and admin "
+ admin + " are not in the same package");
}
+ final CallerIdentity caller = getCallerIdentity(admin);
// Only allow the system user to use this method
- if (!mInjector.binderGetCallingUserHandle().isSystem()) {
- throw new SecurityException("createAndManageUser was called from non-system user");
- }
+ Preconditions.checkCallAuthorization(caller.getUserHandle().isSystem(),
+ "createAndManageUser was called from non-system user");
+ Preconditions.checkCallAuthorization(isDeviceOwner(caller));
final boolean ephemeral = (flags & DevicePolicyManager.MAKE_USER_EPHEMERAL) != 0;
final boolean demo = (flags & DevicePolicyManager.MAKE_USER_DEMO) != 0
&& UserManager.isDeviceInDemoMode(mContext);
@@ -9292,8 +9290,6 @@
// Create user.
UserHandle user = null;
synchronized (getLockObject()) {
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-
final int callingUid = mInjector.binderGetCallingUid();
final long id = mInjector.binderClearCallingIdentity();
try {
@@ -11155,25 +11151,21 @@
@Override
public boolean isActiveDeviceOwner(int uid) {
- synchronized (getLockObject()) {
- return getActiveAdminWithPolicyForUidLocked(
- null, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER, uid) != null;
- }
+ return isDeviceOwner(new CallerIdentity(uid, null, null));
}
@Override
public boolean isActiveProfileOwner(int uid) {
- synchronized (getLockObject()) {
- return getActiveAdminWithPolicyForUidLocked(
- null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, uid) != null;
- }
+ return isProfileOwner(new CallerIdentity(uid, null, null));
}
@Override
public boolean isActiveSupervisionApp(int uid) {
+ if (!isProfileOwner(new CallerIdentity(uid, null, null))) {
+ return false;
+ }
synchronized (getLockObject()) {
- final ActiveAdmin admin = getActiveAdminWithPolicyForUidLocked(
- null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, uid);
+ final ActiveAdmin admin = getProfileOwnerAdminLocked(UserHandle.getUserId(uid));
if (admin == null) {
return false;
}
@@ -11699,6 +11691,10 @@
.getPackageName();
try {
String[] pkgs = mInjector.getIPackageManager().getPackagesForUid(appUid);
+ if (pkgs == null) {
+ return false;
+ }
+
for (String pkg : pkgs) {
if (deviceOwnerPackageName.equals(pkg)) {
return true;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 975e226..e116a35 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -76,14 +76,18 @@
import android.server.ServerProtoEnums;
import android.sysprop.VoldProperties;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.EventLog;
+import android.util.IndentingPrintWriter;
import android.util.Pair;
import android.util.Slog;
+import android.util.TimeUtils;
import android.view.contentcapture.ContentCaptureManager;
import com.android.i18n.timezone.ZoneInfoDb;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
@@ -188,14 +192,20 @@
import com.google.android.startop.iorap.IorapForwardingService;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Timer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
-public final class SystemServer {
+/**
+ * Entry point to {@code system_server}.
+ */
+public final class SystemServer implements Dumpable {
private static final String TAG = "SystemServer";
@@ -384,6 +394,9 @@
private Future<?> mZygotePreload;
private Future<?> mBlobStoreServiceStart;
+ private final SystemServerDumper mDumper = new SystemServerDumper();
+
+
/**
* The pending WTF to be logged into dropbox.
*/
@@ -446,6 +459,75 @@
mRuntimeRestart = "1".equals(SystemProperties.get("sys.boot_completed"));
}
+ @Override
+ public void dump(IndentingPrintWriter pw, String[] args) {
+ pw.printf("Runtime restart: %b\n", mRuntimeRestart);
+ pw.printf("Start count: %d\n", mStartCount);
+ pw.print("Runtime start-up time: ");
+ TimeUtils.formatDuration(mRuntimeStartUptime, pw); pw.println();
+ pw.print("Runtime start-elapsed time: ");
+ TimeUtils.formatDuration(mRuntimeStartElapsedTime, pw); pw.println();
+ }
+
+ private final class SystemServerDumper extends Binder {
+
+ @GuardedBy("mDumpables")
+ private final ArrayMap<String, Dumpable> mDumpables = new ArrayMap<>(4);
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ final boolean hasArgs = args != null && args.length > 0;
+
+ synchronized (mDumpables) {
+ if (hasArgs && "--list".equals(args[0])) {
+ final int dumpablesSize = mDumpables.size();
+ for (int i = 0; i < dumpablesSize; i++) {
+ pw.println(mDumpables.keyAt(i));
+ }
+ return;
+ }
+
+ if (hasArgs && "--name".equals(args[0])) {
+ if (args.length < 2) {
+ pw.println("Must pass at least one argument to --name");
+ return;
+ }
+ final String name = args[1];
+ final Dumpable dumpable = mDumpables.get(name);
+ if (dumpable == null) {
+ pw.printf("No dummpable named %s\n", name);
+ return;
+ }
+
+ try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ")) {
+ // Strip --name DUMPABLE from args
+ final String[] actualArgs = Arrays.copyOfRange(args, 2, args.length);
+ dumpable.dump(ipw, actualArgs);
+ }
+ return;
+ }
+
+ final int dumpablesSize = mDumpables.size();
+ try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ")) {
+ for (int i = 0; i < dumpablesSize; i++) {
+ final Dumpable dumpable = mDumpables.valueAt(i);
+ ipw.printf("%s:\n", dumpable.getDumpableName());
+ ipw.increaseIndent();
+ dumpable.dump(ipw, args);
+ ipw.decreaseIndent();
+ ipw.println();
+ }
+ }
+ }
+ }
+
+ private void addDumpable(@NonNull Dumpable dumpable) {
+ synchronized (mDumpables) {
+ mDumpables.put(dumpable.getDumpableName(), dumpable);
+ }
+ }
+ }
+
private void run() {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
try {
@@ -572,13 +654,21 @@
// Call per-process mainline module initialization.
ActivityThread.initializeMainlineModules();
+ // Sets the dumper service
+ ServiceManager.addService("system_server_dumper", mDumper);
+ mDumper.addDumpable(this);
+
// Create the system service manager.
mSystemServiceManager = new SystemServiceManager(mSystemContext);
mSystemServiceManager.setStartInfo(mRuntimeRestart,
mRuntimeStartElapsedTime, mRuntimeStartUptime);
+ mDumper.addDumpable(mSystemServiceManager);
+
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
// Prepare the thread pool for init tasks that can be parallelized
- SystemServerInitThreadPool.start();
+ SystemServerInitThreadPool tp = SystemServerInitThreadPool.start();
+ mDumper.addDumpable(tp);
+
// Attach JVMTI agent if this is a debuggable build and the system property is set.
if (Build.IS_DEBUGGABLE) {
// Property is of the form "library_path=parameters".
@@ -2321,7 +2411,11 @@
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
t.traceBegin("StartCarServiceHelperService");
- mSystemServiceManager.startService(CAR_SERVICE_HELPER_SERVICE_CLASS);
+ final SystemService cshs = mSystemServiceManager
+ .startService(CAR_SERVICE_HELPER_SERVICE_CLASS);
+ if (cshs instanceof Dumpable) {
+ mDumper.addDumpable((Dumpable) cshs);
+ }
t.traceEnd();
}
diff --git a/services/midi/Android.bp b/services/midi/Android.bp
index 6bce5b5..013f23d 100644
--- a/services/midi/Android.bp
+++ b/services/midi/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.midi",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [":services.midi-sources"],
libs: ["services.core"],
}
diff --git a/services/musicrecognition/Android.bp b/services/musicrecognition/Android.bp
index 39b5bb6..fea9efa 100644
--- a/services/musicrecognition/Android.bp
+++ b/services/musicrecognition/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.musicsearch",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [":services.musicsearch-sources"],
libs: ["services.core", "app-compat-annotations"],
}
\ No newline at end of file
diff --git a/services/net/Android.bp b/services/net/Android.bp
index afea1a0..3c9322d 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.net",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [
":net-module-utils-srcs",
":services.net-sources",
diff --git a/services/people/Android.bp b/services/people/Android.bp
index c863f1f..9bdf488 100644
--- a/services/people/Android.bp
+++ b/services/people/Android.bp
@@ -1,6 +1,6 @@
java_library_static {
name: "services.people",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: ["java/**/*.java"],
libs: ["services.core"],
}
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 7803e78..3e06194 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -22,6 +22,7 @@
import android.annotation.WorkerThread;
import android.app.Notification;
import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.app.Person;
import android.app.people.ConversationChannel;
@@ -98,6 +99,7 @@
private static final String TAG = "DataManager";
+ private static final long RECENT_NOTIFICATIONS_MAX_AGE_MS = 10 * DateUtils.DAY_IN_MILLIS;
private static final long QUERY_EVENTS_MAX_AGE_MS = 5L * DateUtils.MINUTE_IN_MILLIS;
private static final long USAGE_STATS_QUERY_INTERVAL_SEC = 120L;
@VisibleForTesting static final int MAX_CACHED_RECENT_SHORTCUTS = 30;
@@ -232,8 +234,12 @@
if (shortcutInfo == null || parentChannel == null) {
return;
}
+ NotificationChannelGroup parentChannelGroup =
+ mNotificationManagerInternal.getNotificationChannelGroup(packageName,
+ uid, parentChannel.getId());
conversationChannels.add(
- new ConversationChannel(shortcutInfo, parentChannel,
+ new ConversationChannel(shortcutInfo, uid, parentChannel,
+ parentChannelGroup,
conversationInfo.getLastEventTimestamp(),
hasActiveNotifications(packageName, userId, shortcutId)));
});
@@ -259,6 +265,14 @@
* notifications.
*/
public void removeAllRecentConversations(@UserIdInt int callingUserId) {
+ pruneOldRecentConversations(callingUserId, Long.MAX_VALUE);
+ }
+
+ /**
+ * Uncaches the shortcuts for all the recent conversations that haven't been interacted with
+ * recently.
+ */
+ public void pruneOldRecentConversations(@UserIdInt int callingUserId, long currentTimeMs) {
forPackagesInProfile(callingUserId, packageData -> {
String packageName = packageData.getPackageName();
int userId = packageData.getUserId();
@@ -266,12 +280,16 @@
packageData.forAllConversations(conversationInfo -> {
String shortcutId = conversationInfo.getShortcutId();
if (isCachedRecentConversation(conversationInfo)
+ && (currentTimeMs - conversationInfo.getLastEventTimestamp()
+ > RECENT_NOTIFICATIONS_MAX_AGE_MS)
&& !hasActiveNotifications(packageName, userId, shortcutId)) {
idsToUncache.add(shortcutId);
}
});
- mShortcutServiceInternal.uncacheShortcuts(callingUserId, mContext.getPackageName(),
- packageName, idsToUncache, userId, ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ if (!idsToUncache.isEmpty()) {
+ mShortcutServiceInternal.uncacheShortcuts(callingUserId, mContext.getPackageName(),
+ packageName, idsToUncache, userId, ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ }
});
}
@@ -371,6 +389,7 @@
packageData.getEventStore().deleteEventHistories(EventStore.CATEGORY_SMS);
}
packageData.pruneOrphanEvents();
+ pruneOldRecentConversations(userId, System.currentTimeMillis());
cleanupCachedShortcuts(userId, MAX_CACHED_RECENT_SHORTCUTS);
});
}
diff --git a/services/print/Android.bp b/services/print/Android.bp
index 93b5ef0..be5f082 100644
--- a/services/print/Android.bp
+++ b/services/print/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.print",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [":services.print-sources"],
libs: ["services.core"],
}
diff --git a/services/profcollect/Android.bp b/services/profcollect/Android.bp
index b7be5d4..7f5f623 100644
--- a/services/profcollect/Android.bp
+++ b/services/profcollect/Android.bp
@@ -30,7 +30,7 @@
java_library_static {
name: "services.profcollect",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [":services.profcollect-sources"],
libs: ["services.core"],
}
diff --git a/services/restrictions/Android.bp b/services/restrictions/Android.bp
index 2883095..60d161d 100644
--- a/services/restrictions/Android.bp
+++ b/services/restrictions/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.restrictions",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [":services.restrictions-sources"],
libs: ["services.core"],
}
diff --git a/services/startop/Android.bp b/services/startop/Android.bp
index 46a81aae..157408f 100644
--- a/services/startop/Android.bp
+++ b/services/startop/Android.bp
@@ -16,7 +16,7 @@
java_library_static {
name: "services.startop",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
static_libs: [
// frameworks/base/startop/iorap
diff --git a/services/systemcaptions/Android.bp b/services/systemcaptions/Android.bp
index 54968c0..54a5a79 100644
--- a/services/systemcaptions/Android.bp
+++ b/services/systemcaptions/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.systemcaptions",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [":services.systemcaptions-sources"],
libs: ["services.core"],
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 18bd6b1..b98021b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -16,6 +16,7 @@
package com.android.server.job.controllers;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
@@ -27,6 +28,7 @@
import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
import static com.android.server.job.JobSchedulerService.RARE_INDEX;
+import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
@@ -39,6 +41,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
@@ -68,6 +71,7 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.provider.DeviceConfig;
import android.util.SparseBooleanArray;
import androidx.test.runner.AndroidJUnit4;
@@ -78,6 +82,7 @@
import com.android.server.job.JobServiceContext;
import com.android.server.job.JobStore;
import com.android.server.job.controllers.QuotaController.ExecutionStats;
+import com.android.server.job.controllers.QuotaController.QcConstants;
import com.android.server.job.controllers.QuotaController.TimingSession;
import com.android.server.usage.AppStandbyInternal;
@@ -86,16 +91,19 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
import java.time.Clock;
import java.time.Duration;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
@RunWith(AndroidJUnit4.class)
public class QuotaControllerTest {
@@ -113,6 +121,7 @@
private QuotaController.QcConstants mQcConstants;
private int mSourceUid;
private IUidObserver mUidObserver;
+ DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder;
private MockitoSession mMockingSession;
@Mock
@@ -133,6 +142,7 @@
mMockingSession = mockitoSession()
.initMocks(this)
.strictness(Strictness.LENIENT)
+ .spyStatic(DeviceConfig.class)
.mockStatic(LocalServices.class)
.startMocking();
@@ -164,6 +174,18 @@
// Used in QuotaController.Handler.
mJobStore = JobStore.initAndGetForTesting(mContext, mContext.getFilesDir());
when(mJobSchedulerService.getJobStore()).thenReturn(mJobStore);
+ // Used in QuotaController.QcConstants
+ doAnswer((Answer<Void>) invocationOnMock -> null)
+ .when(() -> DeviceConfig.addOnPropertiesChangedListener(
+ anyString(), any(Executor.class),
+ any(DeviceConfig.OnPropertiesChangedListener.class)));
+ mDeviceConfigPropertiesBuilder =
+ new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
+ doAnswer(
+ (Answer<DeviceConfig.Properties>) invocationOnMock
+ -> mDeviceConfigPropertiesBuilder.build())
+ .when(() -> DeviceConfig.getProperties(
+ eq(DeviceConfig.NAMESPACE_JOB_SCHEDULER), ArgumentMatchers.<String>any()));
// Freeze the clocks at 24 hours after this moment in time. Several tests create sessions
// in the past, and QuotaController sometimes floors values at 0, so if the test time
@@ -313,6 +335,18 @@
return new TimingSession(start, start + duration, count);
}
+ private void setDeviceConfigLong(String key, long val) {
+ mQuotaController.prepareForUpdatedConstantsLocked();
+ mDeviceConfigPropertiesBuilder.setLong(key, val);
+ mQcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
+ }
+
+ private void setDeviceConfigInt(String key, int val) {
+ mQuotaController.prepareForUpdatedConstantsLocked();
+ mDeviceConfigPropertiesBuilder.setInt(key, val);
+ mQcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
+ }
+
@Test
public void testSaveTimingSession() {
assertNull(mQuotaController.getTimingSessions(0, "com.android.test"));
@@ -858,8 +892,7 @@
advanceElapsedClock(40 * MINUTE_IN_MILLIS);
}
- mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 0;
- mQcConstants.updateConstants();
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 0);
mQuotaController.invalidateAllExecutionStatsLocked();
assertEquals(0, mQuotaController.getExecutionStatsLocked(
@@ -871,8 +904,7 @@
assertEquals(160, mQuotaController.getExecutionStatsLocked(
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
- mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 500;
- mQcConstants.updateConstants();
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 500);
mQuotaController.invalidateAllExecutionStatsLocked();
assertEquals(0, mQuotaController.getExecutionStatsLocked(
@@ -884,8 +916,7 @@
assertEquals(110, mQuotaController.getExecutionStatsLocked(
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
- mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 1000;
- mQcConstants.updateConstants();
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1000);
mQuotaController.invalidateAllExecutionStatsLocked();
assertEquals(0, mQuotaController.getExecutionStatsLocked(
@@ -897,8 +928,8 @@
assertEquals(110, mQuotaController.getExecutionStatsLocked(
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
- mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 5 * SECOND_IN_MILLIS;
- mQcConstants.updateConstants();
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ 5 * SECOND_IN_MILLIS);
mQuotaController.invalidateAllExecutionStatsLocked();
assertEquals(0, mQuotaController.getExecutionStatsLocked(
@@ -910,8 +941,8 @@
assertEquals(70, mQuotaController.getExecutionStatsLocked(
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
- mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = MINUTE_IN_MILLIS;
- mQcConstants.updateConstants();
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ MINUTE_IN_MILLIS);
mQuotaController.invalidateAllExecutionStatsLocked();
assertEquals(0, mQuotaController.getExecutionStatsLocked(
@@ -923,8 +954,8 @@
assertEquals(20, mQuotaController.getExecutionStatsLocked(
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
- mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 5 * MINUTE_IN_MILLIS;
- mQcConstants.updateConstants();
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ 5 * MINUTE_IN_MILLIS);
mQuotaController.invalidateAllExecutionStatsLocked();
assertEquals(0, mQuotaController.getExecutionStatsLocked(
@@ -936,8 +967,8 @@
assertEquals(10, mQuotaController.getExecutionStatsLocked(
0, "com.android.test", RARE_INDEX).sessionCountInWindow);
- mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 15 * MINUTE_IN_MILLIS;
- mQcConstants.updateConstants();
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ 15 * MINUTE_IN_MILLIS);
mQuotaController.invalidateAllExecutionStatsLocked();
assertEquals(0, mQuotaController.getExecutionStatsLocked(
@@ -951,8 +982,7 @@
// QuotaController caps the duration at 15 minutes, so there shouldn't be any difference
// between an hour and 15 minutes.
- mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = HOUR_IN_MILLIS;
- mQcConstants.updateConstants();
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, HOUR_IN_MILLIS);
mQuotaController.invalidateAllExecutionStatsLocked();
assertEquals(0, mQuotaController.getExecutionStatsLocked(
@@ -994,8 +1024,7 @@
// Advance clock so that the working stats shouldn't be the same.
advanceElapsedClock(MINUTE_IN_MILLIS);
// Change frequent bucket size so that the stats need to be recalculated.
- mQcConstants.WINDOW_SIZE_FREQUENT_MS = 6 * HOUR_IN_MILLIS;
- mQcConstants.updateConstants();
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, 6 * HOUR_IN_MILLIS);
ExecutionStats expectedStats = new ExecutionStats();
expectedStats.windowSizeMs = originalStatsActive.windowSizeMs;
@@ -1413,11 +1442,10 @@
public void testIsWithinQuotaLocked_TimingSession() {
setDischarging();
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQcConstants.MAX_SESSION_COUNT_RARE = 3;
- mQcConstants.MAX_SESSION_COUNT_FREQUENT = 4;
- mQcConstants.MAX_SESSION_COUNT_WORKING = 5;
- mQcConstants.MAX_SESSION_COUNT_ACTIVE = 6;
- mQcConstants.updateConstants();
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RARE, 3);
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_FREQUENT, 4);
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, 5);
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_ACTIVE, 6);
for (int i = 0; i < 7; ++i) {
mQuotaController.saveTimingSession(0, "com.android.test",
@@ -1629,8 +1657,7 @@
final int standbyBucket = RARE_INDEX;
// Prevent timing session throttling from affecting the test.
- mQcConstants.MAX_SESSION_COUNT_RARE = 50;
- mQcConstants.updateConstants();
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RARE, 50);
// No sessions saved yet.
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
@@ -1749,9 +1776,8 @@
public void testMaybeScheduleStartAlarmLocked_JobCount_RateLimitingWindow() {
// Set rate limiting period different from allowed time to confirm code sets based on
// the former.
- mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = 10 * MINUTE_IN_MILLIS;
- mQcConstants.RATE_LIMITING_WINDOW_MS = 5 * MINUTE_IN_MILLIS;
- mQcConstants.updateConstants();
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 10 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_RATE_LIMITING_WINDOW_MS, 5 * MINUTE_IN_MILLIS);
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
final int standbyBucket = WORKING_INDEX;
@@ -1790,8 +1816,9 @@
@Test
public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedBufferSize() {
// Make sure any new value is used correctly.
- mQcConstants.IN_QUOTA_BUFFER_MS *= 2;
- mQcConstants.updateConstants();
+ setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS,
+ mQcConstants.IN_QUOTA_BUFFER_MS * 2);
+
runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck();
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck();
@@ -1800,8 +1827,9 @@
@Test
public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedAllowedTime() {
// Make sure any new value is used correctly.
- mQcConstants.ALLOWED_TIME_PER_PERIOD_MS /= 2;
- mQcConstants.updateConstants();
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS,
+ mQcConstants.ALLOWED_TIME_PER_PERIOD_MS / 2);
+
runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck();
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck();
@@ -1810,8 +1838,9 @@
@Test
public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedMaxTime() {
// Make sure any new value is used correctly.
- mQcConstants.MAX_EXECUTION_TIME_MS /= 2;
- mQcConstants.updateConstants();
+ setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS,
+ mQcConstants.MAX_EXECUTION_TIME_MS / 2);
+
runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck();
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck();
@@ -1820,10 +1849,13 @@
@Test
public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedEverything() {
// Make sure any new value is used correctly.
- mQcConstants.IN_QUOTA_BUFFER_MS *= 2;
- mQcConstants.ALLOWED_TIME_PER_PERIOD_MS /= 2;
- mQcConstants.MAX_EXECUTION_TIME_MS /= 2;
- mQcConstants.updateConstants();
+ setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS,
+ mQcConstants.IN_QUOTA_BUFFER_MS * 2);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS,
+ mQcConstants.ALLOWED_TIME_PER_PERIOD_MS / 2);
+ setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS,
+ mQcConstants.MAX_EXECUTION_TIME_MS / 2);
+
runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck();
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck();
@@ -1891,27 +1923,30 @@
@Test
public void testConstantsUpdating_ValidValues() {
- mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = 5 * MINUTE_IN_MILLIS;
- mQcConstants.IN_QUOTA_BUFFER_MS = 2 * MINUTE_IN_MILLIS;
- mQcConstants.WINDOW_SIZE_ACTIVE_MS = 15 * MINUTE_IN_MILLIS;
- mQcConstants.WINDOW_SIZE_WORKING_MS = 30 * MINUTE_IN_MILLIS;
- mQcConstants.WINDOW_SIZE_FREQUENT_MS = 45 * MINUTE_IN_MILLIS;
- mQcConstants.WINDOW_SIZE_RARE_MS = 60 * MINUTE_IN_MILLIS;
- mQcConstants.MAX_EXECUTION_TIME_MS = 3 * HOUR_IN_MILLIS;
- mQcConstants.MAX_JOB_COUNT_ACTIVE = 5000;
- mQcConstants.MAX_JOB_COUNT_WORKING = 4000;
- mQcConstants.MAX_JOB_COUNT_FREQUENT = 3000;
- mQcConstants.MAX_JOB_COUNT_RARE = 2000;
- mQcConstants.RATE_LIMITING_WINDOW_MS = 15 * MINUTE_IN_MILLIS;
- mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = 500;
- mQcConstants.MAX_SESSION_COUNT_ACTIVE = 500;
- mQcConstants.MAX_SESSION_COUNT_WORKING = 400;
- mQcConstants.MAX_SESSION_COUNT_FREQUENT = 300;
- mQcConstants.MAX_SESSION_COUNT_RARE = 200;
- mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 50;
- mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 10 * SECOND_IN_MILLIS;
-
- mQcConstants.updateConstants();
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 5 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 2 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 15 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 30 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, 45 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RARE_MS, 60 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RESTRICTED_MS, 120 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, 3 * HOUR_IN_MILLIS);
+ setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_ACTIVE, 5000);
+ setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 4000);
+ setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_FREQUENT, 3000);
+ setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_RARE, 2000);
+ setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_RESTRICTED, 2000);
+ setDeviceConfigLong(QcConstants.KEY_RATE_LIMITING_WINDOW_MS, 15 * MINUTE_IN_MILLIS);
+ setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW, 500);
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_ACTIVE, 500);
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, 400);
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_FREQUENT, 300);
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RARE, 200);
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RESTRICTED, 100);
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW, 50);
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ 10 * SECOND_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, 7 * MINUTE_IN_MILLIS);
assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
@@ -1920,6 +1955,8 @@
assertEquals(45 * MINUTE_IN_MILLIS,
mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
assertEquals(60 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
+ assertEquals(120 * MINUTE_IN_MILLIS,
+ mQuotaController.getBucketWindowSizes()[RESTRICTED_INDEX]);
assertEquals(3 * HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getRateLimitingWindowMs());
assertEquals(500, mQuotaController.getMaxJobCountPerRateLimitingWindow());
@@ -1927,39 +1964,44 @@
assertEquals(4000, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
assertEquals(3000, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
assertEquals(2000, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
+ assertEquals(2000, mQuotaController.getBucketMaxJobCounts()[RESTRICTED_INDEX]);
assertEquals(50, mQuotaController.getMaxSessionCountPerRateLimitingWindow());
assertEquals(500, mQuotaController.getBucketMaxSessionCounts()[ACTIVE_INDEX]);
assertEquals(400, mQuotaController.getBucketMaxSessionCounts()[WORKING_INDEX]);
assertEquals(300, mQuotaController.getBucketMaxSessionCounts()[FREQUENT_INDEX]);
assertEquals(200, mQuotaController.getBucketMaxSessionCounts()[RARE_INDEX]);
+ assertEquals(100, mQuotaController.getBucketMaxSessionCounts()[RESTRICTED_INDEX]);
assertEquals(10 * SECOND_IN_MILLIS,
mQuotaController.getTimingSessionCoalescingDurationMs());
+ assertEquals(7 * MINUTE_IN_MILLIS, mQuotaController.getMinQuotaCheckDelayMs());
}
@Test
public void testConstantsUpdating_InvalidValues() {
// Test negatives/too low.
- mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = -MINUTE_IN_MILLIS;
- mQcConstants.IN_QUOTA_BUFFER_MS = -MINUTE_IN_MILLIS;
- mQcConstants.WINDOW_SIZE_ACTIVE_MS = -MINUTE_IN_MILLIS;
- mQcConstants.WINDOW_SIZE_WORKING_MS = -MINUTE_IN_MILLIS;
- mQcConstants.WINDOW_SIZE_FREQUENT_MS = -MINUTE_IN_MILLIS;
- mQcConstants.WINDOW_SIZE_RARE_MS = -MINUTE_IN_MILLIS;
- mQcConstants.MAX_EXECUTION_TIME_MS = -MINUTE_IN_MILLIS;
- mQcConstants.MAX_JOB_COUNT_ACTIVE = -1;
- mQcConstants.MAX_JOB_COUNT_WORKING = 1;
- mQcConstants.MAX_JOB_COUNT_FREQUENT = 1;
- mQcConstants.MAX_JOB_COUNT_RARE = 1;
- mQcConstants.RATE_LIMITING_WINDOW_MS = 15 * SECOND_IN_MILLIS;
- mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = 0;
- mQcConstants.MAX_SESSION_COUNT_ACTIVE = -1;
- mQcConstants.MAX_SESSION_COUNT_WORKING = 0;
- mQcConstants.MAX_SESSION_COUNT_FREQUENT = -3;
- mQcConstants.MAX_SESSION_COUNT_RARE = 0;
- mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 0;
- mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = -1;
-
- mQcConstants.updateConstants();
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, -MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, -MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, -MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, -MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, -MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RARE_MS, -MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RESTRICTED_MS, -MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, -MINUTE_IN_MILLIS);
+ setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_ACTIVE, -1);
+ setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 1);
+ setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_FREQUENT, 1);
+ setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_RARE, 1);
+ setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_RESTRICTED, -1);
+ setDeviceConfigLong(QcConstants.KEY_RATE_LIMITING_WINDOW_MS, 15 * SECOND_IN_MILLIS);
+ setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW, 0);
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_ACTIVE, -1);
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, 0);
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_FREQUENT, -3);
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RARE, 0);
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RESTRICTED, -5);
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW, 0);
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, -1);
+ setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, -1);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
assertEquals(0, mQuotaController.getInQuotaBufferMs());
@@ -1967,6 +2009,7 @@
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
+ assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RESTRICTED_INDEX]);
assertEquals(HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
assertEquals(30 * SECOND_IN_MILLIS, mQuotaController.getRateLimitingWindowMs());
assertEquals(10, mQuotaController.getMaxJobCountPerRateLimitingWindow());
@@ -1974,35 +2017,37 @@
assertEquals(10, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
assertEquals(10, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
assertEquals(10, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
+ assertEquals(10, mQuotaController.getBucketMaxJobCounts()[RESTRICTED_INDEX]);
assertEquals(10, mQuotaController.getMaxSessionCountPerRateLimitingWindow());
assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[ACTIVE_INDEX]);
assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[WORKING_INDEX]);
assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[FREQUENT_INDEX]);
assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[RARE_INDEX]);
+ assertEquals(0, mQuotaController.getBucketMaxSessionCounts()[RESTRICTED_INDEX]);
assertEquals(0, mQuotaController.getTimingSessionCoalescingDurationMs());
+ assertEquals(0, mQuotaController.getMinQuotaCheckDelayMs());
// Invalid configurations.
// In_QUOTA_BUFFER should never be greater than ALLOWED_TIME_PER_PERIOD
- mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = 2 * MINUTE_IN_MILLIS;
- mQcConstants.IN_QUOTA_BUFFER_MS = 5 * MINUTE_IN_MILLIS;
-
- mQcConstants.updateConstants();
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 2 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 5 * MINUTE_IN_MILLIS);
assertTrue(mQuotaController.getInQuotaBufferMs()
<= mQuotaController.getAllowedTimePerPeriodMs());
// Test larger than a day. Controller should cap at one day.
- mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = 25 * HOUR_IN_MILLIS;
- mQcConstants.IN_QUOTA_BUFFER_MS = 25 * HOUR_IN_MILLIS;
- mQcConstants.WINDOW_SIZE_ACTIVE_MS = 25 * HOUR_IN_MILLIS;
- mQcConstants.WINDOW_SIZE_WORKING_MS = 25 * HOUR_IN_MILLIS;
- mQcConstants.WINDOW_SIZE_FREQUENT_MS = 25 * HOUR_IN_MILLIS;
- mQcConstants.WINDOW_SIZE_RARE_MS = 25 * HOUR_IN_MILLIS;
- mQcConstants.MAX_EXECUTION_TIME_MS = 25 * HOUR_IN_MILLIS;
- mQcConstants.RATE_LIMITING_WINDOW_MS = 25 * HOUR_IN_MILLIS;
- mQcConstants.TIMING_SESSION_COALESCING_DURATION_MS = 25 * HOUR_IN_MILLIS;
-
- mQcConstants.updateConstants();
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RARE_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RESTRICTED_MS, 30 * 24 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_RATE_LIMITING_WINDOW_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, 25 * HOUR_IN_MILLIS);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
@@ -2010,10 +2055,13 @@
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
+ assertEquals(7 * 24 * HOUR_IN_MILLIS,
+ mQuotaController.getBucketWindowSizes()[RESTRICTED_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getRateLimitingWindowMs());
assertEquals(15 * MINUTE_IN_MILLIS,
mQuotaController.getTimingSessionCoalescingDurationMs());
+ assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getMinQuotaCheckDelayMs());
}
/** Tests that TimingSessions aren't saved when the device is charging. */
@@ -2619,9 +2667,9 @@
doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
// Essentially disable session throttling.
- mQcConstants.MAX_SESSION_COUNT_WORKING =
- mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = Integer.MAX_VALUE;
- mQcConstants.updateConstants();
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, Integer.MAX_VALUE);
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
+ Integer.MAX_VALUE);
final int standbyBucket = WORKING_INDEX;
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
@@ -2671,12 +2719,12 @@
doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
// Essentially disable job count throttling.
- mQcConstants.MAX_JOB_COUNT_FREQUENT =
- mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = Integer.MAX_VALUE;
+ setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_FREQUENT, Integer.MAX_VALUE);
+ setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
+ Integer.MAX_VALUE);
// Make sure throttling is because of COUNT_PER_RATE_LIMITING_WINDOW.
- mQcConstants.MAX_SESSION_COUNT_FREQUENT =
- mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW + 1;
- mQcConstants.updateConstants();
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_FREQUENT,
+ mQcConstants.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW + 1);
final int standbyBucket = FREQUENT_INDEX;
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java
index 29d3f29..d7fef60 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java
@@ -34,7 +34,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
import com.android.server.location.listeners.ListenerMultiplexer.UpdateServiceLock;
import org.junit.Before;
@@ -59,6 +58,9 @@
void onRegistrationAdded(Consumer<TestListenerRegistration> consumer,
TestListenerRegistration registration);
+ void onRegistrationReplaced(Consumer<TestListenerRegistration> consumer,
+ TestListenerRegistration oldRegistration, TestListenerRegistration newRegistration);
+
void onRegistrationRemoved(Consumer<TestListenerRegistration> consumer,
TestListenerRegistration registration);
@@ -90,11 +92,42 @@
assertThat(mMultiplexer.mRegistered).isTrue();
assertThat(mMultiplexer.mMergedRequest).isEqualTo(0);
+ mMultiplexer.addListener(1, consumer);
+ mInOrder.verify(mCallbacks).onRegistrationRemoved(eq(consumer),
+ any(TestListenerRegistration.class));
+ mInOrder.verify(mCallbacks).onRegistrationReplaced(eq(consumer),
+ any(TestListenerRegistration.class), any(TestListenerRegistration.class));
+ assertThat(mMultiplexer.mRegistered).isTrue();
+ assertThat(mMultiplexer.mMergedRequest).isEqualTo(1);
+
mMultiplexer.notifyListeners();
verify(consumer).accept(any(TestListenerRegistration.class));
}
@Test
+ public void testReplace() {
+ Consumer<TestListenerRegistration> oldConsumer = mock(Consumer.class);
+ Consumer<TestListenerRegistration> consumer = mock(Consumer.class);
+
+ mMultiplexer.addListener(0, oldConsumer);
+ mInOrder.verify(mCallbacks).onRegister();
+ mInOrder.verify(mCallbacks).onRegistrationAdded(eq(oldConsumer),
+ any(TestListenerRegistration.class));
+ mInOrder.verify(mCallbacks).onActive();
+ mMultiplexer.replaceListener(1, oldConsumer, consumer);
+ mInOrder.verify(mCallbacks).onRegistrationRemoved(eq(oldConsumer),
+ any(TestListenerRegistration.class));
+ mInOrder.verify(mCallbacks).onRegistrationReplaced(eq(consumer),
+ any(TestListenerRegistration.class), any(TestListenerRegistration.class));
+ assertThat(mMultiplexer.mRegistered).isTrue();
+ assertThat(mMultiplexer.mMergedRequest).isEqualTo(1);
+
+ mMultiplexer.notifyListeners();
+ verify(consumer).accept(any(TestListenerRegistration.class));
+ verify(oldConsumer, never()).accept(any(TestListenerRegistration.class));
+ }
+
+ @Test
public void testRemove() {
Consumer<TestListenerRegistration> consumer = mock(Consumer.class);
@@ -319,8 +352,7 @@
}
private static class TestListenerRegistration extends
- RequestListenerRegistration<Integer, Consumer<TestListenerRegistration>,
- ListenerOperation<Consumer<TestListenerRegistration>>> {
+ RequestListenerRegistration<Integer, Consumer<TestListenerRegistration>> {
boolean mActive = true;
@@ -332,9 +364,7 @@
private static class TestMultiplexer extends
ListenerMultiplexer<Consumer<TestListenerRegistration>,
- Consumer<TestListenerRegistration>,
- ListenerOperation<Consumer<TestListenerRegistration>>, TestListenerRegistration,
- Integer> {
+ Consumer<TestListenerRegistration>, TestListenerRegistration, Integer> {
boolean mRegistered;
int mMergedRequest;
@@ -351,7 +381,13 @@
}
public void addListener(Integer request, Consumer<TestListenerRegistration> consumer) {
- addRegistration(consumer, new TestListenerRegistration(request, consumer));
+ putRegistration(consumer, new TestListenerRegistration(request, consumer));
+ }
+
+ public void replaceListener(Integer request, Consumer<TestListenerRegistration> oldConsumer,
+ Consumer<TestListenerRegistration> consumer) {
+ replaceRegistration(oldConsumer, consumer,
+ new TestListenerRegistration(request, consumer));
}
public void removeListener(Consumer<TestListenerRegistration> consumer) {
@@ -422,6 +458,13 @@
}
@Override
+ protected void onRegistrationReplaced(Consumer<TestListenerRegistration> consumer,
+ TestListenerRegistration oldRegistration,
+ TestListenerRegistration newRegistration) {
+ mCallbacks.onRegistrationReplaced(consumer, oldRegistration, newRegistration);
+ }
+
+ @Override
protected void onRegistrationRemoved(Consumer<TestListenerRegistration> consumer,
TestListenerRegistration registration) {
mCallbacks.onRegistrationRemoved(consumer, registration);
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index d2d85c8..e76c5a4 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -46,7 +46,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;
import org.junit.Before;
@@ -158,16 +157,16 @@
}
@Test
- public void testIsPanicButtonGestureEnabled_settingDisabled() {
- withPanicGestureEnabledSettingValue(false);
- assertFalse(mGestureLauncherService.isPanicButtonGestureEnabled(
+ public void testIsEmergencyGestureEnabled_settingDisabled() {
+ withEmergencyGestureEnabledSettingValue(false);
+ assertFalse(mGestureLauncherService.isEmergencyGestureEnabled(
mContext, FAKE_USER_ID));
}
@Test
- public void testIsPanicButtonGestureEnabled_settingEnabled() {
- withPanicGestureEnabledSettingValue(true);
- assertTrue(mGestureLauncherService.isPanicButtonGestureEnabled(
+ public void testIsEmergencyGestureEnabled_settingEnabled() {
+ withEmergencyGestureEnabledSettingValue(true);
+ assertTrue(mGestureLauncherService.isEmergencyGestureEnabled(
mContext, FAKE_USER_ID));
}
@@ -181,10 +180,10 @@
}
@Test
- public void testHandlePanicGesture_userSetupComplete() {
+ public void testHandleEmergencyGesture_userSetupComplete() {
withUserSetupCompleteValue(true);
- assertTrue(mGestureLauncherService.handlePanicButtonGesture());
+ assertTrue(mGestureLauncherService.handleEmergencyGesture());
}
@Test
@@ -196,10 +195,10 @@
}
@Test
- public void testHandlePanicGesture_userSetupNotComplete() {
+ public void testHandleEmergencyGesture_userSetupNotComplete() {
withUserSetupCompleteValue(false);
- assertFalse(mGestureLauncherService.handlePanicButtonGesture());
+ assertFalse(mGestureLauncherService.handleEmergencyGesture());
}
@Test
@@ -223,9 +222,9 @@
}
@Test
- public void testInterceptPowerKeyDown_firstPowerDown_panicGestureNotLaunched() {
- withPanicGestureEnabledSettingValue(true);
- mGestureLauncherService.updatePanicButtonGestureEnabled();
+ public void testInterceptPowerKeyDown_firstPowerDown_emergencyGestureNotLaunched() {
+ withEmergencyGestureEnabledSettingValue(true);
+ mGestureLauncherService.updateEmergencyGestureEnabled();
long eventTime = INITIAL_EVENT_TIME_MILLIS
+ GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS - 1;
@@ -425,12 +424,12 @@
@Test
public void
- testInterceptPowerKeyDown_fiveInboundPresses_cameraAndPanicEnabled_bothLaunch() {
+ testInterceptPowerKeyDown_fiveInboundPresses_cameraAndEmergencyEnabled_bothLaunch() {
withCameraDoubleTapPowerEnableConfigValue(true);
withCameraDoubleTapPowerDisableSettingValue(0);
- withPanicGestureEnabledSettingValue(true);
+ withEmergencyGestureEnabledSettingValue(true);
mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
- mGestureLauncherService.updatePanicButtonGestureEnabled();
+ mGestureLauncherService.updateEmergencyGestureEnabled();
withUserSetupCompleteValue(true);
// First button press does nothing
@@ -476,7 +475,7 @@
assertEquals(1, tapCounts.get(0).intValue());
assertEquals(2, tapCounts.get(1).intValue());
- // Continue the button presses for the panic gesture.
+ // Continue the button presses for the emergency gesture.
// Presses 3 and 4 should not trigger any gesture
for (int i = 0; i < 2; i++) {
@@ -490,7 +489,7 @@
assertFalse(outLaunched.value);
}
- // Fifth button press should trigger the panic flow
+ // Fifth button press should trigger the emergency flow
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -513,9 +512,9 @@
@Test
public void
- testInterceptPowerKeyDown_fiveInboundPresses_panicGestureEnabled_launchesPanicFlow() {
- withPanicGestureEnabledSettingValue(true);
- mGestureLauncherService.updatePanicButtonGestureEnabled();
+ testInterceptPowerKeyDown_fiveInboundPresses_emergencyGestureEnabled_launchesFlow() {
+ withEmergencyGestureEnabledSettingValue(true);
+ mGestureLauncherService.updateEmergencyGestureEnabled();
withUserSetupCompleteValue(true);
// First button press does nothing
@@ -542,7 +541,7 @@
assertFalse(outLaunched.value);
}
- // Fifth button press should trigger the panic flow
+ // Fifth button press should trigger the emergency flow
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -565,9 +564,9 @@
@Test
public void
- testInterceptPowerKeyDown_tenInboundPresses_panicGestureEnabled_pressesIntercepted() {
- withPanicGestureEnabledSettingValue(true);
- mGestureLauncherService.updatePanicButtonGestureEnabled();
+ testInterceptPowerKeyDown_tenInboundPresses_emergencyGestureEnabled_keyIntercepted() {
+ withEmergencyGestureEnabledSettingValue(true);
+ mGestureLauncherService.updateEmergencyGestureEnabled();
withUserSetupCompleteValue(true);
// First button press does nothing
@@ -594,7 +593,7 @@
assertFalse(outLaunched.value);
}
- // Fifth button press should trigger the panic flow
+ // Fifth button press should trigger the emergency flow
eventTime += interval;
keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
IGNORED_REPEAT);
@@ -1128,10 +1127,10 @@
UserHandle.USER_CURRENT);
}
- private void withPanicGestureEnabledSettingValue(boolean enable) {
+ private void withEmergencyGestureEnabledSettingValue(boolean enable) {
Settings.Secure.putIntForUser(
mContentResolver,
- Settings.Secure.PANIC_GESTURE_ENABLED,
+ Settings.Secure.EMERGENCY_GESTURE_ENABLED,
enable ? 1 : 0,
UserHandle.USER_CURRENT);
}
diff --git a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
index 8a22a2f..044bdba 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
@@ -16,10 +16,16 @@
package com.android.server;
+import static com.android.server.testutils.TestUtils.assertExpectException;
+
import static org.junit.Assert.assertArrayEquals;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.os.CombinedVibrationEffect;
+import android.os.Process;
+import android.os.VibrationAttributes;
+import android.os.VibrationEffect;
import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
@@ -40,9 +46,16 @@
@Presubmit
public class VibratorManagerServiceTest {
- @Rule public MockitoRule rule = MockitoJUnit.rule();
+ private static final int UID = Process.ROOT_UID;
+ private static final String PACKAGE_NAME = "package";
+ private static final VibrationAttributes ALARM_ATTRS =
+ new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build();
- @Mock private VibratorManagerService.NativeWrapper mNativeWrapperMock;
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Mock
+ private VibratorManagerService.NativeWrapper mNativeWrapperMock;
@Before
public void setUp() throws Exception {
@@ -72,7 +85,26 @@
@Test
public void getVibratorIds_withNonEmptyResultFromNative_returnsSameArray() {
- when(mNativeWrapperMock.getVibratorIds()).thenReturn(new int[]{ 1, 2 });
- assertArrayEquals(new int[]{ 1, 2 }, createService().getVibratorIds());
+ when(mNativeWrapperMock.getVibratorIds()).thenReturn(new int[]{1, 2});
+ assertArrayEquals(new int[]{1, 2}, createService().getVibratorIds());
+ }
+
+ @Test
+ public void vibrate_isUnsupported() {
+ VibratorManagerService service = createService();
+ CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
+ assertExpectException(UnsupportedOperationException.class,
+ "Not implemented",
+ () -> service.vibrate(UID, PACKAGE_NAME, effect, ALARM_ATTRS, "reason", service));
+ }
+
+ @Test
+ public void cancelVibrate_isUnsupported() {
+ VibratorManagerService service = createService();
+ CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
+ assertExpectException(UnsupportedOperationException.class,
+ "Not implemented", () -> service.cancelVibrate(service));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
index d57fd4b..3b4699e 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
@@ -62,6 +62,8 @@
.getContext();
doReturn(mServiceThreadRule.getThread().getThreadHandler()).when(mMockInjector)
.getUiHandler(any());
+ final ProcessList dummyList = new ProcessList();
+ doReturn(dummyList).when(mMockInjector).getProcessList(any());
mAms = new ActivityManagerService(mMockInjector, mServiceThreadRule.getThread());
mAmi = mAms.new LocalService();
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 241b5a9..b360ae8 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -81,6 +81,7 @@
import com.android.server.wm.ActivityTaskManagerService;
import org.junit.After;
+import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
@@ -128,11 +129,14 @@
sPackageManagerInternal = mock(PackageManagerInternal.class);
doReturn(new ComponentName("", "")).when(sPackageManagerInternal)
.getSystemUiServiceComponent();
- // Remove stale instance of PackageManagerInternal if there is any
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, sPackageManagerInternal);
}
+ @AfterClass
+ public static void tearDownOnce() {
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ }
+
@Rule public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule();
private Context mContext = getInstrumentation().getTargetContext();
diff --git a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
index 4221575..693bc7d 100644
--- a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
@@ -23,7 +23,7 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -68,6 +68,7 @@
private ActivityManagerService mAms;
@Mock private Context mContext;
+ @Mock private Resources mResources;
private MockContentResolver mContentResolver;
private CoreSettingsObserver mCoreSettingsObserver;
@@ -94,7 +95,10 @@
mContentResolver = new MockContentResolver(mContext);
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
when(mContext.getContentResolver()).thenReturn(mContentResolver);
- when(mContext.getResources()).thenReturn(mock(Resources.class));
+ when(mContext.getResources()).thenReturn(mResources);
+ // To prevent NullPointerException at the constructor of ActivityManagerConstants.
+ when(mResources.getStringArray(anyInt())).thenReturn(new String[0]);
+ when(mResources.getIntArray(anyInt())).thenReturn(new int[0]);
mAms = new ActivityManagerService(new TestInjector(mContext),
mServiceThreadRule.getThread());
diff --git a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
index b2d7177..4f58c87 100644
--- a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
@@ -35,6 +35,7 @@
import com.android.server.LocalServices;
import com.android.server.wm.ActivityTaskManagerService;
+import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -63,8 +64,6 @@
sPackageManagerInternal = mock(PackageManagerInternal.class);
doReturn(new ComponentName("", "")).when(sPackageManagerInternal)
.getSystemUiServiceComponent();
- // Remove stale instance of PackageManagerInternal if there is any
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, sPackageManagerInternal);
// We need to run with dexmaker share class loader to make use of
@@ -78,13 +77,18 @@
sService.mConstants = new ActivityManagerConstants(sContext, sService,
sContext.getMainThreadHandler());
sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList, null);
- LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
LocalServices.addService(UsageStatsManagerInternal.class,
mock(UsageStatsManagerInternal.class));
sService.mUsageStatsService = LocalServices.getService(UsageStatsManagerInternal.class);
});
}
+ @AfterClass
+ public static void tearDownOnce() {
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
+ }
+
@Before
public void setUpProcess() {
// Need to run with dexmaker share class loader to mock package private class.
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index c4f7b95..30b1b3e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2814,7 +2814,7 @@
exerciseUserProvisioningTransitions(CALLER_USER_HANDLE,
DevicePolicyManager.STATE_USER_PROFILE_COMPLETE,
- DevicePolicyManager.STATE_USER_PROFILE_FINALIZED);
+ DevicePolicyManager.STATE_USER_UNMANAGED);
}
public void testSetUserProvisioningState_managedProfileFromSetupWizard_managedProfile()
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 058794a..9b182a7 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -105,6 +105,30 @@
}
@Test
+ public void requestOverrideState() {
+ mService.setOverrideState(OTHER_DEVICE_STATE);
+ // Committed state changes as there is a requested override.
+ assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
+
+ // Committed state is set back to the requested state once the override is cleared.
+ mService.clearOverrideState();
+ assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+ }
+
+ @Test
+ public void requestOverrideState_unsupportedState() {
+ mService.setOverrideState(UNSUPPORTED_DEVICE_STATE);
+ // Committed state remains the same as the override state is unsupported.
+ assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+ }
+
+ @Test
public void supportedStatesChanged() {
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), INVALID_DEVICE_STATE);
@@ -146,6 +170,23 @@
assertEquals(mService.getRequestedState(), OTHER_DEVICE_STATE);
}
+ @Test
+ public void supportedStatesChanged_unsupportedOverrideState() {
+ mService.setOverrideState(OTHER_DEVICE_STATE);
+ // Committed state changes as there is a requested override.
+ assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE);
+
+ mProvider.notifySupportedDeviceStates(new int []{ DEFAULT_DEVICE_STATE });
+
+ // Committed state is set back to the requested state as the override state is no longer
+ // supported.
+ assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getRequestedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
+ }
+
private static final class TestDeviceStatePolicy implements DeviceStatePolicy {
private final DeviceStateProvider mProvider;
private int mLastDeviceStateRequestedToConfigure = INVALID_DEVICE_STATE;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index 2a9c394..8af7332 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -114,7 +114,7 @@
mHdmiControlService.setCecController(hdmiCecController);
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
- mHdmiControlService.initPortInfo();
+ mHdmiControlService.initService();
mPhysicalAddress = 0x2000;
mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
mTestLooper.dispatchAll();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index 1385376..37a75e3 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -123,7 +123,7 @@
hdmiControlService.setCecController(hdmiCecController);
hdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(hdmiControlService));
hdmiControlService.setMessageValidator(new HdmiCecMessageValidator(hdmiControlService));
- hdmiControlService.initPortInfo();
+ hdmiControlService.initService();
mAction = new ArcInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem);
mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index 169f885..6027c3e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -117,7 +117,7 @@
hdmiControlService.setCecController(hdmiCecController);
hdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(hdmiControlService));
hdmiControlService.setMessageValidator(new HdmiCecMessageValidator(hdmiControlService));
- hdmiControlService.initPortInfo();
+ hdmiControlService.initService();
mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
@Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 2c42791..bb57a69 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -30,6 +30,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/** Fake {@link NativeWrapper} useful for testing. */
final class FakeNativeWrapper implements NativeWrapper {
@@ -55,6 +56,7 @@
};
private final List<HdmiCecMessage> mResultMessages = new ArrayList<>();
+ private final Map<Integer, Boolean> mPortConnectionStatus = new HashMap<>();
private final HashMap<Integer, Integer> mMessageSendResult = new HashMap<>();
private int mMyPhysicalAddress = 0;
private HdmiPortInfo[] mHdmiPortInfo = null;
@@ -125,7 +127,12 @@
@Override
public boolean nativeIsConnected(int port) {
- return false;
+ Boolean isConnected = mPortConnectionStatus.get(port);
+ return isConnected == null ? false : isConnected;
+ }
+
+ public void setPortConnectionStatus(int port, boolean connected) {
+ mPortConnectionStatus.put(port, connected);
}
public void onCecMessage(HdmiCecMessage hdmiCecMessage) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
new file mode 100644
index 0000000..a1eb037
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.hdmi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.hardware.hdmi.HdmiControlManager;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings.Global;
+import android.sysprop.HdmiProperties;
+import android.util.Slog;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.hdmi.cec.config.CecSettings;
+import com.android.server.hdmi.cec.config.XmlParser;
+
+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 org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public final class HdmiCecConfigTest {
+ private static final String TAG = "HdmiCecConfigTest";
+
+ private Context mContext;
+
+ @Mock private HdmiCecConfig.StorageAdapter mStorageAdapter;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
+
+ @Test
+ public void getAllCecSettings_NoMasterXml() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(null, null);
+ assertThat(hdmiCecConfig.getAllSettings()).isEmpty();
+ }
+
+ @Test
+ public void getAllCecSettings_Empty() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getAllSettings()).isEmpty();
+ }
+
+ @Test
+ public void getAllCecSettings_BasicSanity() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"hdmi_cec_enabled\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"0\" />"
+ + " <value string-value=\"1\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"1\" />"
+ + " </setting>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"false\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getAllSettings())
+ .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP);
+ }
+
+ @Test
+ public void getUserCecSettings_NoMasterXml() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(null, null);
+ assertThat(hdmiCecConfig.getUserSettings()).isEmpty();
+ }
+
+ @Test
+ public void getUserCecSettings_Empty() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getUserSettings()).isEmpty();
+ }
+
+ @Test
+ public void getUserCecSettings_OnlyMasterXml() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"hdmi_cec_enabled\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"0\" />"
+ + " <value string-value=\"1\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"1\" />"
+ + " </setting>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getUserSettings())
+ .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP);
+ }
+
+ @Test
+ public void getUserCecSettings_WithOverride() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"hdmi_cec_enabled\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"0\" />"
+ + " <value string-value=\"1\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"1\" />"
+ + " </setting>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>",
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"false\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>");
+ assertThat(hdmiCecConfig.getUserSettings())
+ .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED);
+ }
+
+ @Test
+ public void getAllowedValues_NoMasterXml() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(null, null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getAllowedValues("foo"));
+ }
+
+ @Test
+ public void getAllowedValues_InvalidSetting() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getAllowedValues("foo"));
+ }
+
+ @Test
+ public void getAllowedValues_BasicSanity() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getAllowedValues(
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP))
+ .containsExactly(HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV,
+ HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST,
+ HdmiControlManager.SEND_STANDBY_ON_SLEEP_NONE);
+ }
+
+ @Test
+ public void getDefaultValue_NoMasterXml() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(null, null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getDefaultValue("foo"));
+ }
+
+ @Test
+ public void getDefaultValue_InvalidSetting() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getDefaultValue("foo"));
+ }
+
+ @Test
+ public void getDefaultValue_BasicSanity() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getDefaultValue(
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP))
+ .isEqualTo(HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV);
+ }
+
+ @Test
+ public void getValue_NoMasterXml() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(null, null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getValue("foo"));
+ }
+
+ @Test
+ public void getValue_InvalidSetting() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.getValue("foo"));
+ }
+
+ @Test
+ public void getValue_GlobalSetting_BasicSanity() {
+ when(mStorageAdapter.retrieveGlobalSetting(mContext,
+ Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV))
+ .thenReturn(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getValue(
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP))
+ .isEqualTo(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ }
+
+ @Test
+ public void getValue_SystemProperty_BasicSanity() {
+ when(mStorageAdapter.retrieveSystemProperty(
+ HdmiCecConfig.SYSPROP_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiProperties.power_state_change_on_active_source_lost_values
+ .NONE.name().toLowerCase()))
+ .thenReturn(HdmiProperties.power_state_change_on_active_source_lost_values
+ .STANDBY_NOW.name().toLowerCase());
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"power_state_change_on_active_source_lost\""
+ + " user-configurable=\"false\">"
+ + " <allowed-values>"
+ + " <value string-value=\"none\" />"
+ + " <value string-value=\"standby_now\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"none\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThat(hdmiCecConfig.getValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST))
+ .isEqualTo(HdmiProperties.power_state_change_on_active_source_lost_values
+ .STANDBY_NOW.name().toLowerCase());
+ }
+
+ @Test
+ public void setValue_NoMasterXml() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(null, null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.setValue("foo", "bar"));
+ }
+
+ @Test
+ public void setValue_InvalidSetting() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.setValue("foo", "bar"));
+ }
+
+ @Test
+ public void setValue_NotConfigurable() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"false\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.setValue(
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST));
+ }
+
+ @Test
+ public void setValue_InvalidValue() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> hdmiCecConfig.setValue(
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+ "bar"));
+ }
+
+ @Test
+ public void setValue_GlobalSetting_BasicSanity() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"send_standby_on_sleep\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"to_tv\" />"
+ + " <value string-value=\"broadcast\" />"
+ + " <value string-value=\"none\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"to_tv\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ hdmiCecConfig.setValue(HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ verify(mStorageAdapter).storeGlobalSetting(mContext,
+ Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ }
+
+ @Test
+ public void setValue_SystemProperty_BasicSanity() {
+ HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+ + "<cec-settings>"
+ + " <setting name=\"power_state_change_on_active_source_lost\""
+ + " user-configurable=\"true\">"
+ + " <allowed-values>"
+ + " <value string-value=\"none\" />"
+ + " <value string-value=\"standby_now\" />"
+ + " </allowed-values>"
+ + " <default-value string-value=\"none\" />"
+ + " </setting>"
+ + "</cec-settings>", null);
+ hdmiCecConfig.setValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiProperties.power_state_change_on_active_source_lost_values
+ .STANDBY_NOW.name().toLowerCase());
+ verify(mStorageAdapter).storeSystemProperty(
+ HdmiCecConfig.SYSPROP_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiProperties.power_state_change_on_active_source_lost_values
+ .STANDBY_NOW.name().toLowerCase());
+ }
+
+ private HdmiCecConfig createHdmiCecConfig(String productConfigXml, String vendorOverrideXml) {
+ CecSettings productConfig = null;
+ CecSettings vendorOverride = null;
+ try {
+ if (productConfigXml != null) {
+ productConfig = XmlParser.read(
+ new ByteArrayInputStream(productConfigXml.getBytes()));
+ }
+ if (vendorOverrideXml != null) {
+ vendorOverride = XmlParser.read(
+ new ByteArrayInputStream(vendorOverrideXml.getBytes()));
+ }
+ } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+ Slog.e(TAG, "Encountered an error while reading/parsing CEC config strings", e);
+ }
+ return new HdmiCecConfig(mContext, mStorageAdapter, productConfig, vendorOverride);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 74fd683..433f6e7 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -15,13 +15,9 @@
*/
package com.android.server.hdmi;
-import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
-import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE;
-
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
-import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2;
import static com.android.server.hdmi.Constants.ADDR_TUNER_1;
import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.Constants.MESSAGE_GIVE_AUDIO_STATUS;
@@ -33,6 +29,7 @@
import android.content.Context;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
import android.media.AudioManager;
import android.os.Handler;
import android.os.IPowerManager;
@@ -226,7 +223,7 @@
new HdmiPortInfo(
4, HdmiPortInfo.PORT_INPUT, HDMI_3_PHYSICAL_ADDRESS, true, false, false);
mNativeWrapper.setPortInfo(mHdmiPortInfo);
- mHdmiControlService.initPortInfo();
+ mHdmiControlService.initService();
// No TV device interacts with AVR so system audio control won't be turned on here
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
@@ -654,75 +651,6 @@
}
@Test
- public void updateCecDevice_deviceNotExists_addDevice() {
- assertThat(mInvokeDeviceEventState).isNotEqualTo(DEVICE_EVENT_ADD_DEVICE);
- HdmiDeviceInfo newDevice = new HdmiDeviceInfo(
- ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
- Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
-
- mHdmiCecLocalDeviceAudioSystem.updateCecDevice(newDevice);
- assertThat(mDeviceInfo).isEqualTo(newDevice);
- assertThat(mHdmiCecLocalDeviceAudioSystem
- .getCecDeviceInfo(newDevice.getLogicalAddress())).isEqualTo(newDevice);
- assertThat(mInvokeDeviceEventState).isEqualTo(DEVICE_EVENT_ADD_DEVICE);
- }
-
- @Test
- public void updateCecDevice_deviceExists_doNothing() {
- mInvokeDeviceEventState = 0;
- HdmiDeviceInfo oldDevice = new HdmiDeviceInfo(
- ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
- Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
- mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice);
-
- mHdmiCecLocalDeviceAudioSystem.updateCecDevice(oldDevice);
- assertThat(mInvokeDeviceEventState).isEqualTo(0);
- }
-
- @Test
- public void updateCecDevice_deviceInfoDifferent_updateDevice() {
- assertThat(mInvokeDeviceEventState).isNotEqualTo(DEVICE_EVENT_UPDATE_DEVICE);
- HdmiDeviceInfo oldDevice = new HdmiDeviceInfo(
- ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
- Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
- mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice);
-
- HdmiDeviceInfo differentDevice = new HdmiDeviceInfo(
- ADDR_PLAYBACK_1, 0x2300, 4, HdmiDeviceInfo.DEVICE_PLAYBACK,
- Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
-
- mHdmiCecLocalDeviceAudioSystem.updateCecDevice(differentDevice);
- assertThat(mDeviceInfo).isEqualTo(differentDevice);
- assertThat(mHdmiCecLocalDeviceAudioSystem
- .getCecDeviceInfo(differentDevice.getLogicalAddress())).isEqualTo(differentDevice);
- assertThat(mInvokeDeviceEventState).isEqualTo(DEVICE_EVENT_UPDATE_DEVICE);
- }
-
- @Test
- @Ignore("b/120845532")
- public void handleReportPhysicalAddress_differentPath_addDevice() {
- assertThat(mInvokeDeviceEventState).isNotEqualTo(DEVICE_EVENT_ADD_DEVICE);
- HdmiDeviceInfo oldDevice = new HdmiDeviceInfo(
- ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
- Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
- mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice);
-
- HdmiDeviceInfo differentDevice = new HdmiDeviceInfo(
- ADDR_PLAYBACK_2, 0x2200, 1, HdmiDeviceInfo.DEVICE_PLAYBACK,
- Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_2));
- HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder
- .buildReportPhysicalAddressCommand(
- ADDR_PLAYBACK_2, 0x2200, HdmiDeviceInfo.DEVICE_PLAYBACK);
- mHdmiCecLocalDeviceAudioSystem.handleReportPhysicalAddress(reportPhysicalAddress);
-
- mTestLooper.dispatchAll();
- assertThat(mDeviceInfo).isEqualTo(differentDevice);
- assertThat(mHdmiCecLocalDeviceAudioSystem
- .getCecDeviceInfo(differentDevice.getLogicalAddress())).isEqualTo(differentDevice);
- assertThat(mInvokeDeviceEventState).isEqualTo(DEVICE_EVENT_ADD_DEVICE);
- }
-
- @Test
public void doNotWakeUpOnHotPlug_PlugIn() {
mWokenUp = false;
mHdmiCecLocalDeviceAudioSystem.onHotplug(0, true);
@@ -907,4 +835,42 @@
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
assertThat(mHdmiCecLocalDeviceAudioSystem.isActiveSource()).isFalse();
}
+
+ @Test
+ @Ignore("b/151150320")
+ public void oneTouchPlay() {
+ mHdmiControlService.oneTouchPlay(new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ }
+ });
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage textViewOn_fromPlayback = HdmiCecMessageBuilder.buildTextViewOn(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+ HdmiCecMessage activeSource_fromPlayback = HdmiCecMessageBuilder.buildActiveSource(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ SELF_PHYSICAL_ADDRESS);
+ HdmiCecMessage systemAudioModeRequest_fromPlayback =
+ HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_AUDIO_SYSTEM, SELF_PHYSICAL_ADDRESS, true);
+ HdmiCecMessage textViewOn_fromAudioSystem = HdmiCecMessageBuilder.buildTextViewOn(
+ mHdmiCecLocalDeviceAudioSystem.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+ HdmiCecMessage activeSource_fromAudioSystem = HdmiCecMessageBuilder.buildActiveSource(
+ mHdmiCecLocalDeviceAudioSystem.getDeviceInfo().getLogicalAddress(),
+ SELF_PHYSICAL_ADDRESS);
+ HdmiCecMessage systemAudioModeRequest_fromAudioSystem =
+ HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+ mHdmiCecLocalDeviceAudioSystem.getDeviceInfo().getLogicalAddress(),
+ ADDR_AUDIO_SYSTEM, SELF_PHYSICAL_ADDRESS, true);
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn_fromPlayback);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource_fromPlayback);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(
+ systemAudioModeRequest_fromPlayback);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn_fromAudioSystem);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSource_fromAudioSystem);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(
+ systemAudioModeRequest_fromAudioSystem);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index ef98b98..440befc 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -25,6 +25,8 @@
import android.content.Context;
import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
import android.os.Handler;
import android.os.IPowerManager;
import android.os.IThermalService;
@@ -125,7 +127,12 @@
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
mLocalDevices.add(mHdmiCecLocalDevicePlayback);
- mHdmiControlService.initPortInfo();
+ HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
+ hdmiPortInfos[0] =
+ new HdmiPortInfo(1, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
+ mNativeWrapper.setPortInfo(hdmiPortInfos);
+ mNativeWrapper.setPortConnectionStatus(1, true);
+ mHdmiControlService.initService();
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mPlaybackPhysicalAddress = 0x2000;
mNativeWrapper.setPhysicalAddress(mPlaybackPhysicalAddress);
@@ -892,6 +899,7 @@
public void handleSetStreamPath_afterHotplug_broadcastsActiveSource() {
mNativeWrapper.onHotplugEvent(1, false);
mNativeWrapper.onHotplugEvent(1, true);
+ mTestLooper.dispatchAll();
HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
mPlaybackPhysicalAddress);
@@ -967,4 +975,73 @@
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
assertThat(mStandby).isFalse();
}
+
+ @Test
+ public void oneTouchPlay_SendStandbyOnSleepToTv() {
+ mHdmiCecLocalDevicePlayback.mService.writeStringSetting(
+ Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV);
+ mHdmiControlService.oneTouchPlay(new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ }
+ });
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(mPlaybackLogicalAddress,
+ ADDR_TV);
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+ HdmiCecMessage systemAudioModeRequest = HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+ mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM, mPlaybackPhysicalAddress, true);
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(systemAudioModeRequest);
+ }
+
+ @Test
+ public void oneTouchPlay_SendStandbyOnSleepBroadcast() {
+ mHdmiCecLocalDevicePlayback.mService.writeStringSetting(
+ Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ mHdmiControlService.oneTouchPlay(new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ }
+ });
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(mPlaybackLogicalAddress,
+ ADDR_TV);
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+ HdmiCecMessage systemAudioModeRequest = HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+ mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM, mPlaybackPhysicalAddress, true);
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).contains(systemAudioModeRequest);
+ }
+
+ @Test
+ public void oneTouchPlay_SendStandbyOnSleepNone() {
+ mHdmiCecLocalDevicePlayback.mService.writeStringSetting(
+ Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.SEND_STANDBY_ON_SLEEP_NONE);
+ mHdmiControlService.oneTouchPlay(new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ }
+ });
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(mPlaybackLogicalAddress,
+ ADDR_TV);
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+ HdmiCecMessage systemAudioModeRequest = HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+ mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM, mPlaybackPhysicalAddress, true);
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(systemAudioModeRequest);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index ce1cdf3..bf4851b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Handler;
import android.os.IPowerManager;
@@ -103,7 +104,11 @@
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
mLocalDevices.add(mHdmiCecLocalDeviceTv);
- mHdmiControlService.initPortInfo();
+ HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
+ hdmiPortInfos[0] =
+ new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x1000, true, false, false);
+ mNativeWrapper.setPortInfo(hdmiPortInfos);
+ mHdmiControlService.initService();
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTvPhysicalAddress = 0x0000;
mNativeWrapper.setPhysicalAddress(mTvPhysicalAddress);
@@ -119,8 +124,9 @@
@Test
public void onAddressAllocated_invokesDeviceDiscovery() {
+ mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
- mHdmiCecLocalDeviceTv.onAddressAllocated(0, HdmiControlService.INITIATED_BY_BOOT_UP);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index 5a05fc6..debf20b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -17,6 +17,7 @@
package com.android.server.hdmi;
import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_DESTINATION;
+import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER;
import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER_SHORT;
import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_SOURCE;
import static com.android.server.hdmi.HdmiCecMessageValidator.OK;
@@ -71,6 +72,44 @@
assertMessageValidity("04:90").isEqualTo(ERROR_PARAMETER_SHORT);
}
+ @Test
+ public void isValid_setMenuLanguage() {
+ assertMessageValidity("4F:32:53:50:41").isEqualTo(OK);
+ assertMessageValidity("0F:32:45:4E:47:8C:49:D3:48").isEqualTo(OK);
+
+ assertMessageValidity("40:32:53:50:41").isEqualTo(ERROR_DESTINATION);
+ assertMessageValidity("F0:32").isEqualTo(ERROR_SOURCE);
+ assertMessageValidity("4F:32:45:55").isEqualTo(ERROR_PARAMETER_SHORT);
+ assertMessageValidity("4F:32:19:7F:83").isEqualTo(ERROR_PARAMETER);
+ }
+
+ @Test
+ public void isValid_setOsdString() {
+ assertMessageValidity("40:64:80:41").isEqualTo(OK);
+ // Even though the parameter string in this message is longer than 14 bytes, it is accepted
+ // as this parameter might be extended in future versions.
+ assertMessageValidity("04:64:00:4C:69:76:69:6E:67:52:6F:6F:6D:20:54:56:C4").isEqualTo(OK);
+
+ assertMessageValidity("4F:64:40:41").isEqualTo(ERROR_DESTINATION);
+ assertMessageValidity("F0:64:C0:41").isEqualTo(ERROR_SOURCE);
+ assertMessageValidity("40:64:00").isEqualTo(ERROR_PARAMETER_SHORT);
+ // Invalid Display Control
+ assertMessageValidity("40:64:20:4C:69:76").isEqualTo(ERROR_PARAMETER);
+ // Invalid ASCII characters
+ assertMessageValidity("40:64:40:4C:69:7F").isEqualTo(ERROR_PARAMETER);
+ }
+
+ @Test
+ public void isValid_setOsdName() {
+ assertMessageValidity("40:47:4C:69:76:69:6E:67:52:6F:6F:6D:54:56").isEqualTo(OK);
+ assertMessageValidity("40:47:54:56").isEqualTo(OK);
+
+ assertMessageValidity("4F:47:54:56").isEqualTo(ERROR_DESTINATION);
+ assertMessageValidity("F0:47:54:56").isEqualTo(ERROR_SOURCE);
+ assertMessageValidity("40:47").isEqualTo(ERROR_PARAMETER_SHORT);
+ assertMessageValidity("40:47:4C:69:7F").isEqualTo(ERROR_PARAMETER);
+ }
+
private IntegerSubject assertMessageValidity(String message) {
return assertThat(mHdmiCecMessageValidator.isValid(buildMessage(message)));
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
new file mode 100644
index 0000000..080b52b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
@@ -0,0 +1,449 @@
+/*
+ * 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.hdmi;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiPortInfo;
+import android.os.Looper;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for {@link HdmiCecNetwork} class.
+ */
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class HdmiCecNetworkTest {
+
+ private HdmiCecNetwork mHdmiCecNetwork;
+
+ private Context mContext;
+
+ private HdmiControlService mHdmiControlService;
+ private HdmiMhlControllerStub mHdmiMhlControllerStub;
+
+ private HdmiCecController mHdmiCecController;
+ private FakeNativeWrapper mNativeWrapper;
+ private Looper mMyLooper;
+ private TestLooper mTestLooper = new TestLooper();
+ private HdmiPortInfo[] mHdmiPortInfo;
+ private List<Integer> mDeviceEventListenerStatuses = new ArrayList<>();
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mHdmiControlService = new HdmiControlService(mContext) {
+ @Override
+ void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
+ mDeviceEventListenerStatuses.add(status);
+ }
+ };
+
+ mMyLooper = mTestLooper.getLooper();
+ mHdmiControlService.setIoLooper(mMyLooper);
+
+ mNativeWrapper = new FakeNativeWrapper();
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(mHdmiControlService,
+ mNativeWrapper, mHdmiControlService.getAtomWriter());
+ mHdmiMhlControllerStub = HdmiMhlControllerStub.create(mHdmiControlService);
+ mHdmiControlService.setCecController(mHdmiCecController);
+ mHdmiControlService.setHdmiMhlController(mHdmiMhlControllerStub);
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+
+ mHdmiCecNetwork = new HdmiCecNetwork(mHdmiControlService,
+ mHdmiCecController, mHdmiMhlControllerStub);
+
+ mHdmiControlService.setHdmiCecNetwork(mHdmiCecNetwork);
+
+ mHdmiPortInfo = new HdmiPortInfo[5];
+ mHdmiPortInfo[0] =
+ new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x2100, true, false, false);
+ mHdmiPortInfo[1] =
+ new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2200, true, false, false);
+ mHdmiPortInfo[2] =
+ new HdmiPortInfo(3, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, false);
+ mHdmiPortInfo[3] =
+ new HdmiPortInfo(4, HdmiPortInfo.PORT_INPUT, 0x3000, true, false, false);
+ mHdmiPortInfo[4] =
+ new HdmiPortInfo(5, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
+ mNativeWrapper.setPortInfo(mHdmiPortInfo);
+ mHdmiCecNetwork.initPortInfo();
+ }
+
+ @Test
+ public void initializeNetwork_verifyPortInfo() {
+ mHdmiCecNetwork.initPortInfo();
+ assertThat(mHdmiCecNetwork.getPortInfo()).hasSize(mHdmiPortInfo.length);
+ }
+
+ @Test
+ public void physicalAddressToPort_pathExists_weAreNonTv() {
+ mNativeWrapper.setPhysicalAddress(0x2000);
+ mHdmiCecNetwork.initPortInfo();
+ assertThat(mHdmiCecNetwork.physicalAddressToPortId(0x2120)).isEqualTo(1);
+ assertThat(mHdmiCecNetwork.physicalAddressToPortId(0x2234)).isEqualTo(2);
+ }
+
+ @Test
+ public void physicalAddressToPort_pathExists_weAreSourceDevice() {
+ mNativeWrapper.setPhysicalAddress(0x2000);
+ mHdmiCecNetwork.initPortInfo();
+ assertThat(mHdmiCecNetwork.physicalAddressToPortId(0x0000)).isEqualTo(5);
+ }
+
+ @Test
+ public void physicalAddressToPort_pathExists_weAreTv() {
+ mNativeWrapper.setPhysicalAddress(0x0000);
+ mHdmiCecNetwork.initPortInfo();
+ assertThat(mHdmiCecNetwork.physicalAddressToPortId(0x2120)).isEqualTo(3);
+ assertThat(mHdmiCecNetwork.physicalAddressToPortId(0x3234)).isEqualTo(4);
+ }
+
+ @Test
+ public void physicalAddressToPort_pathInvalid() {
+ mNativeWrapper.setPhysicalAddress(0x2000);
+ mHdmiCecNetwork.initPortInfo();
+ assertThat(mHdmiCecNetwork.physicalAddressToPortId(0x1000)).isEqualTo(
+ Constants.INVALID_PORT_ID);
+ }
+
+ @Test
+ public void localDevices_verifyOne_tv() {
+ mHdmiCecNetwork.addLocalDevice(HdmiDeviceInfo.DEVICE_TV,
+ new HdmiCecLocalDeviceTv(mHdmiControlService));
+
+ assertThat(mHdmiCecNetwork.getLocalDeviceList()).hasSize(1);
+ assertThat(mHdmiCecNetwork.getLocalDeviceList().get(0)).isInstanceOf(
+ HdmiCecLocalDeviceTv.class);
+ assertThat(mHdmiCecNetwork.getLocalDevice(HdmiDeviceInfo.DEVICE_TV)).isNotNull();
+ assertThat(mHdmiCecNetwork.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK)).isNull();
+ }
+
+ @Test
+ public void localDevices_verifyOne_playback() {
+ mHdmiCecNetwork.addLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK,
+ new HdmiCecLocalDevicePlayback(mHdmiControlService));
+
+ assertThat(mHdmiCecNetwork.getLocalDeviceList()).hasSize(1);
+ assertThat(mHdmiCecNetwork.getLocalDeviceList().get(0)).isInstanceOf(
+ HdmiCecLocalDevicePlayback.class);
+ assertThat(mHdmiCecNetwork.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK)).isNotNull();
+ assertThat(mHdmiCecNetwork.getLocalDevice(HdmiDeviceInfo.DEVICE_TV)).isNull();
+ }
+
+ @Test
+ public void cecDevices_tracking_logicalAddressOnly() throws Exception {
+ int logicalAddress = Constants.ADDR_PLAYBACK_1;
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildActiveSource(logicalAddress, 0x1000));
+
+ assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+
+ HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
+ assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
+ assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
+ Constants.INVALID_PHYSICAL_ADDRESS);
+ assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
+ assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
+ HdmiUtils.getDefaultDeviceName(logicalAddress));
+ assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
+ assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(
+ HdmiControlManager.POWER_STATUS_UNKNOWN);
+
+ assertThat(mDeviceEventListenerStatuses).containsExactly(
+ HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
+ }
+
+ @Test
+ public void cecDevices_tracking_logicalAddressOnly_doesntNotifyAgain() throws Exception {
+ int logicalAddress = Constants.ADDR_PLAYBACK_1;
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildActiveSource(logicalAddress, 0x1000));
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildActiveSource(logicalAddress, 0x1000));
+
+ assertThat(mDeviceEventListenerStatuses).containsExactly(
+ HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
+ }
+
+ @Test
+ public void cecDevices_tracking_reportPhysicalAddress() {
+ int logicalAddress = Constants.ADDR_PLAYBACK_1;
+ int physicalAddress = 0x1000;
+ int type = HdmiDeviceInfo.DEVICE_PLAYBACK;
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(logicalAddress,
+ physicalAddress, type));
+
+ assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+
+ HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
+ assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
+ assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
+ physicalAddress);
+ assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(type);
+ assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
+ HdmiUtils.getDefaultDeviceName(logicalAddress));
+ assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
+ assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(
+ HdmiControlManager.POWER_STATUS_UNKNOWN);
+ }
+
+ @Test
+ public void cecDevices_tracking_updateDeviceInfo_sameDoesntNotify() {
+ int logicalAddress = Constants.ADDR_PLAYBACK_1;
+ int physicalAddress = 0x1000;
+ int type = HdmiDeviceInfo.DEVICE_PLAYBACK;
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildActiveSource(logicalAddress, 0x1000));
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(logicalAddress,
+ physicalAddress, type));
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(logicalAddress,
+ physicalAddress, type));
+
+
+ // ADD for logical address first detected
+ // UPDATE for updating device with physical address
+ assertThat(mDeviceEventListenerStatuses).containsExactly(
+ HdmiControlManager.DEVICE_EVENT_ADD_DEVICE,
+ HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
+ }
+
+ @Test
+ public void cecDevices_tracking_reportPowerStatus() {
+ int logicalAddress = Constants.ADDR_PLAYBACK_1;
+ int powerStatus = HdmiControlManager.POWER_STATUS_ON;
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildReportPowerStatus(logicalAddress,
+ Constants.ADDR_BROADCAST, powerStatus));
+
+ assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+
+ HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
+ assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
+ assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
+ Constants.INVALID_PHYSICAL_ADDRESS);
+ assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
+ assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
+ assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
+ HdmiUtils.getDefaultDeviceName(logicalAddress));
+ assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(powerStatus);
+ }
+
+ @Test
+ public void cecDevices_tracking_reportOsdName() {
+ int logicalAddress = Constants.ADDR_PLAYBACK_1;
+ String osdName = "Test Device";
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildSetOsdNameCommand(logicalAddress,
+ Constants.ADDR_BROADCAST, osdName));
+
+ assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+
+ HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
+ assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
+ assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
+ Constants.INVALID_PHYSICAL_ADDRESS);
+ assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
+ assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
+ assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(osdName);
+ assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(
+ HdmiControlManager.POWER_STATUS_UNKNOWN);
+ }
+
+ @Test
+ public void cecDevices_tracking_reportVendorId() {
+ int logicalAddress = Constants.ADDR_PLAYBACK_1;
+ int vendorId = 1234;
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildDeviceVendorIdCommand(logicalAddress, vendorId));
+
+ assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+
+ HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
+ assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
+ assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
+ Constants.INVALID_PHYSICAL_ADDRESS);
+ assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
+ assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
+ HdmiUtils.getDefaultDeviceName(logicalAddress));
+ assertThat(cecDeviceInfo.getVendorId()).isEqualTo(vendorId);
+ assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(
+ HdmiControlManager.POWER_STATUS_UNKNOWN);
+ }
+
+ @Test
+ public void cecDevices_tracking_updatesDeviceInfo() {
+ int logicalAddress = Constants.ADDR_PLAYBACK_1;
+ int physicalAddress = 0x1000;
+ int type = HdmiDeviceInfo.DEVICE_PLAYBACK;
+ int powerStatus = HdmiControlManager.POWER_STATUS_ON;
+ String osdName = "Test Device";
+ int vendorId = 1234;
+
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(logicalAddress,
+ physicalAddress, type));
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildReportPowerStatus(logicalAddress,
+ Constants.ADDR_BROADCAST, powerStatus));
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildSetOsdNameCommand(logicalAddress,
+ Constants.ADDR_BROADCAST, osdName));
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildDeviceVendorIdCommand(logicalAddress, vendorId));
+
+ assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+
+ HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
+ assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
+ assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(physicalAddress);
+ assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(type);
+ assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(osdName);
+ assertThat(cecDeviceInfo.getVendorId()).isEqualTo(vendorId);
+ assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(powerStatus);
+ }
+
+ @Test
+ public void cecDevices_tracking_updatesPhysicalAddress() {
+ int logicalAddress = Constants.ADDR_PLAYBACK_1;
+ int initialPhysicalAddress = 0x1000;
+ int updatedPhysicalAddress = 0x2000;
+ int type = HdmiDeviceInfo.DEVICE_PLAYBACK;
+
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(logicalAddress,
+ initialPhysicalAddress, type));
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(logicalAddress,
+ updatedPhysicalAddress, type));
+ assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+
+ HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
+ assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
+ assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(updatedPhysicalAddress);
+ assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(type);
+
+ // ADD for logical address first detected
+ // UPDATE for updating device with physical address
+ // UPDATE for updating device with new physical address
+ assertThat(mDeviceEventListenerStatuses).containsExactly(
+ HdmiControlManager.DEVICE_EVENT_ADD_DEVICE,
+ HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE,
+ HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
+ }
+
+ @Test
+ public void cecDevices_tracking_updatesPowerStatus() {
+ int logicalAddress = Constants.ADDR_PLAYBACK_1;
+ int powerStatus = HdmiControlManager.POWER_STATUS_ON;
+ int updatedPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
+
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildReportPowerStatus(logicalAddress,
+ Constants.ADDR_BROADCAST, powerStatus));
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildReportPowerStatus(logicalAddress,
+ Constants.ADDR_BROADCAST, updatedPowerStatus));
+
+ assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+
+ HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
+ assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
+ assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(updatedPowerStatus);
+ }
+
+ @Test
+ public void cecDevices_tracking_updatesOsdName() {
+ int logicalAddress = Constants.ADDR_PLAYBACK_1;
+ String osdName = "Test Device";
+ String updatedOsdName = "Different";
+
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildSetOsdNameCommand(logicalAddress,
+ Constants.ADDR_BROADCAST, osdName));
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildSetOsdNameCommand(logicalAddress,
+ Constants.ADDR_BROADCAST, updatedOsdName));
+
+ assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+
+ HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
+ assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
+ assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(updatedOsdName);
+ }
+
+ @Test
+ public void cecDevices_tracking_updatesVendorId() {
+ int logicalAddress = Constants.ADDR_PLAYBACK_1;
+ int vendorId = 1234;
+ int updatedVendorId = 12345;
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildDeviceVendorIdCommand(logicalAddress, vendorId));
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildDeviceVendorIdCommand(logicalAddress, updatedVendorId));
+
+ assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+
+ HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
+ assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
+ assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
+ Constants.INVALID_PHYSICAL_ADDRESS);
+ assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
+ assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
+ HdmiUtils.getDefaultDeviceName(logicalAddress));
+ assertThat(cecDeviceInfo.getVendorId()).isEqualTo(updatedVendorId);
+ assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(
+ HdmiControlManager.POWER_STATUS_UNKNOWN);
+ }
+
+ @Test
+ public void cecDevices_tracking_clearDevices() {
+ int logicalAddress = Constants.ADDR_PLAYBACK_1;
+ mHdmiCecNetwork.handleCecMessage(
+ HdmiCecMessageBuilder.buildActiveSource(logicalAddress, 0x1000));
+
+ assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+
+ mHdmiCecNetwork.clearDeviceList();
+
+ assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).isEmpty();
+
+ assertThat(mDeviceEventListenerStatuses).containsExactly(
+ HdmiControlManager.DEVICE_EVENT_ADD_DEVICE,
+ HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java
index c4068d3..74a0052 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java
@@ -21,11 +21,13 @@
import static junit.framework.Assert.assertEquals;
+import android.content.Context;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.os.Looper;
import android.os.test.TestLooper;
+import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -44,6 +46,8 @@
@RunWith(JUnit4.class)
public class HdmiControlServiceBinderAPITest {
+ private Context mContext;
+
private class HdmiCecLocalDeviceMyDevice extends HdmiCecLocalDevice {
private boolean mCanGoToStandby;
@@ -111,8 +115,12 @@
@Before
public void SetUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ // Some tests expect no logical addresses being allocated at the beginning of the test.
+ setHdmiControlEnabled(false);
+
mHdmiControlService =
- new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ new HdmiControlService(mContext) {
@Override
void sendCecCommand(HdmiCecMessage command) {
switch (command.getOpcode()) {
@@ -164,7 +172,7 @@
mHdmiPortInfo[0] =
new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x2100, true, false, false);
mNativeWrapper.setPortInfo(mHdmiPortInfo);
- mHdmiControlService.initPortInfo();
+ mHdmiControlService.initService();
mResult = -1;
mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
@@ -183,6 +191,7 @@
assertEquals(mResult, -1);
assertThat(mPlaybackDevice.isActiveSource()).isFalse();
+ setHdmiControlEnabled(true);
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
assertThat(mHdmiControlService.isAddressAllocated()).isTrue();
@@ -192,6 +201,8 @@
@Test
public void oneTouchPlay_addressAllocated() {
+ setHdmiControlEnabled(true);
+
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
assertThat(mHdmiControlService.isAddressAllocated()).isTrue();
@@ -204,4 +215,10 @@
assertEquals(mResult, HdmiControlManager.RESULT_SUCCESS);
assertThat(mPlaybackDevice.isActiveSource()).isTrue();
}
+
+ private void setHdmiControlEnabled(boolean enabled) {
+ int value = enabled ? 1 : 0;
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.HDMI_CONTROL_ENABLED,
+ value);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 2f48b5e..8f631a3 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -176,7 +176,7 @@
mHdmiPortInfo[3] =
new HdmiPortInfo(4, HdmiPortInfo.PORT_INPUT, 0x3000, true, false, false);
mNativeWrapper.setPortInfo(mHdmiPortInfo);
- mHdmiControlService.initPortInfo();
+ mHdmiControlService.initService();
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
@@ -206,29 +206,6 @@
}
@Test
- public void pathToPort_pathExists_weAreNonTv() {
- mNativeWrapper.setPhysicalAddress(0x2000);
- mHdmiControlService.initPortInfo();
- assertThat(mHdmiControlService.pathToPortId(0x2120)).isEqualTo(1);
- assertThat(mHdmiControlService.pathToPortId(0x2234)).isEqualTo(2);
- }
-
- @Test
- public void pathToPort_pathExists_weAreTv() {
- mNativeWrapper.setPhysicalAddress(0x0000);
- mHdmiControlService.initPortInfo();
- assertThat(mHdmiControlService.pathToPortId(0x2120)).isEqualTo(3);
- assertThat(mHdmiControlService.pathToPortId(0x3234)).isEqualTo(4);
- }
-
- @Test
- public void pathToPort_pathInvalid() {
- mNativeWrapper.setPhysicalAddress(0x2000);
- mHdmiControlService.initPortInfo();
- assertThat(mHdmiControlService.pathToPortId(0x1000)).isEqualTo(Constants.INVALID_PORT_ID);
- }
-
- @Test
public void initialPowerStatus_normalBoot_isTransientToStandby() {
assertThat(mHdmiControlService.getInitialPowerStatus()).isEqualTo(
HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index 6be28d9..0e4bfab 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -90,8 +90,6 @@
break;
case Constants.MESSAGE_INITIATE_ARC:
break;
- default:
- throw new IllegalArgumentException("Unexpected message");
}
}
@@ -159,6 +157,14 @@
return -1;
}
};
+
+ Looper looper = mTestLooper.getLooper();
+ hdmiControlService.setIoLooper(looper);
+ HdmiCecController.NativeWrapper nativeWrapper = new FakeNativeWrapper();
+ HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
+ hdmiControlService, nativeWrapper, hdmiControlService.getAtomWriter());
+ hdmiControlService.setCecController(hdmiCecController);
+ hdmiControlService.initService();
mHdmiCecLocalDeviceAudioSystem =
new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
@Override
@@ -181,8 +187,6 @@
}
};
mHdmiCecLocalDeviceAudioSystem.init();
- Looper looper = mTestLooper.getLooper();
- hdmiControlService.setIoLooper(looper);
}
@Test
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 9ce4ee0..f823bb9 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
@@ -74,6 +74,7 @@
import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
+import android.text.format.DateUtils;
import android.util.Range;
import com.android.internal.app.ChooserActivity;
@@ -860,6 +861,30 @@
}
@Test
+ public void testPruneOldRecentConversations() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+ when(mNotification.getShortcutId()).thenReturn(TEST_SHORTCUT_ID);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+ listenerService.onNotificationRemoved(mStatusBarNotification, null,
+ NotificationListenerService.REASON_CLICK);
+
+ mDataManager.pruneOldRecentConversations(USER_ID_PRIMARY,
+ System.currentTimeMillis() + (10 * DateUtils.DAY_IN_MILLIS) + 1);
+
+ verify(mShortcutServiceInternal).uncacheShortcuts(
+ anyInt(), any(), eq(TEST_PKG_NAME), eq(Collections.singletonList(TEST_SHORTCUT_ID)),
+ eq(USER_ID_PRIMARY), eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
+ }
+
+ @Test
public void testGetLastInteraction() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
index 72afca0..9b25d0d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -45,6 +45,7 @@
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.TestPackageParser2;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import org.junit.Before;
import org.junit.Test;
@@ -288,6 +289,31 @@
assertThat(mApexManager.isApkInApexInstallSuccess(activeApex.apexModuleName)).isFalse();
}
+ /**
+ * registerApkInApex method checks if the prefix of base apk path contains the apex package
+ * name. When an apex package name is a prefix of another apex package name, e.g,
+ * com.android.media and com.android.mediaprovider, then we need to ensure apk inside apex
+ * mediaprovider does not get registered under apex media.
+ */
+ @Test
+ public void testRegisterApkInApexDoesNotRegisterSimilarPrefix() throws RemoteException {
+ when(mApexService.getActivePackages()).thenReturn(createApexInfo(true, true));
+ final ApexManager.ActiveApexInfo activeApex = mApexManager.getActiveApexInfos().get(0);
+ assertThat(activeApex.apexModuleName).isEqualTo(TEST_APEX_PKG);
+
+ AndroidPackage fakeApkInApex = mock(AndroidPackage.class);
+ when(fakeApkInApex.getBaseApkPath()).thenReturn("/apex/" + TEST_APEX_PKG + "randomSuffix");
+ when(fakeApkInApex.getPackageName()).thenReturn("randomPackageName");
+
+ when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, true));
+ mApexManager.scanApexPackagesTraced(mPackageParser2,
+ ParallelPackageParser.makeExecutorService());
+
+ assertThat(mApexManager.getApksInApex(activeApex.apexModuleName)).isEmpty();
+ mApexManager.registerApkInApex(fakeApkInApex);
+ assertThat(mApexManager.getApksInApex(activeApex.apexModuleName)).isEmpty();
+ }
+
private ApexInfo[] createApexInfo(boolean isActive, boolean isFactory) {
File apexFile = extractResource(TEST_APEX_PKG, TEST_APEX_FILE_NAME);
ApexInfo apexInfo = new ApexInfo();
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 e4acdfe..f78c01a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -177,6 +177,12 @@
}
@Override
+ public Context createContextAsUser(UserHandle user, int flags) {
+ when(mMockPackageManager.getUserId()).thenReturn(user.getIdentifier());
+ return this;
+ }
+
+ @Override
public void unregisterReceiver(BroadcastReceiver receiver) {
// ignore.
}
@@ -939,7 +945,7 @@
assertEquals(Process.SYSTEM_UID, mInjectedCallingUid);
final String packageName = (String) pmInvocation.getArguments()[0];
- final int userId = (Integer) pmInvocation.getArguments()[1];
+ final int userId = mMockPackageManager.getUserId();
final Resources res = mock(Resources.class);
@@ -971,7 +977,7 @@
return Integer.parseInt(entryName.substring(1)) + ressIdOffset;
}).when(res).getIdentifier(anyStringOrNull(), anyStringOrNull(), anyStringOrNull());
return res;
- }).when(mMockPackageManager).getResourcesForApplicationAsUser(anyString(), anyInt());
+ }).when(mMockPackageManager).getResourcesForApplication(anyString());
}
protected static UserInfo withProfileGroupId(UserInfo in, int groupId) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
index 86758f1..40d959d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
@@ -222,4 +222,18 @@
assertTrue(mFullyLoadedCalled.block(WAIT_TIMEOUT_MILLIS));
assertFalse(mIncrementalStates.isLoading());
}
+
+ /**
+ * Test startability transitions if app crashes or anrs
+ */
+ @Test
+ public void testStartableTransition_AppCrashOrAnr() {
+ mIncrementalStates.onCrashOrAnr();
+ assertFalse(mIncrementalStates.isStartable());
+ mIncrementalStates.setProgress(1.0f);
+ assertTrue(mIncrementalStates.isStartable());
+ mIncrementalStates.onCrashOrAnr();
+ // Test that if fully loaded, app remains startable even if it has crashed
+ assertTrue(mIncrementalStates.isStartable());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index 35d6f47..d54a40e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -16,12 +16,21 @@
package com.android.server.pm;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static java.lang.reflect.Modifier.isFinal;
+import static java.lang.reflect.Modifier.isPublic;
+import static java.lang.reflect.Modifier.isStatic;
+
import android.content.IIntentReceiver;
+import android.content.pm.PackageManagerInternal;
import android.os.Bundle;
import android.util.SparseArray;
import androidx.test.runner.AndroidJUnit4;
+import com.google.android.collect.Lists;
+
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -29,6 +38,12 @@
import org.junit.runner.RunWith;
import java.io.File;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
// runtest -c com.android.server.pm.PackageManagerServiceTest frameworks-services
// bit FrameworksServicesTests:com.android.server.pm.PackageManagerServiceTest
@@ -133,4 +148,51 @@
}
}
}
+
+ @Test
+ public void testKnownPackageToString_shouldNotGetUnknown() {
+ final List<String> packageNames = new ArrayList<>();
+ for (int i = 0; i <= PackageManagerInternal.LAST_KNOWN_PACKAGE; i++) {
+ packageNames.add(PackageManagerInternal.knownPackageToString(i));
+ }
+ assertWithMessage(
+ "The Ids of KnownPackage should be continuous and the string representation "
+ + "should not be unknown.").that(
+ packageNames).containsNoneIn(Lists.newArrayList("Unknown"));
+ }
+
+ @Test
+ public void testKnownPackage_lastKnownPackageIsTheLast() throws Exception {
+ final List<Integer> knownPackageIds = getKnownPackageIdsList();
+ assertWithMessage(
+ "The last KnownPackage Id should be assigned to PackageManagerInternal"
+ + ".LAST_KNOWN_PACKAGE.").that(
+ knownPackageIds.get(knownPackageIds.size() - 1)).isEqualTo(
+ PackageManagerInternal.LAST_KNOWN_PACKAGE);
+ }
+
+ @Test
+ public void testKnownPackage_IdsShouldBeUniqueAndContinuous() throws Exception {
+ final List<Integer> knownPackageIds = getKnownPackageIdsList();
+ for (int i = 0, size = knownPackageIds.size(); i < size - 1; i++) {
+ assertWithMessage(
+ "The KnownPackage Ids should be unique and continuous. KnownPackageIds = "
+ + Arrays.toString(knownPackageIds.toArray())).that(
+ knownPackageIds.get(i) + 1).isEqualTo(knownPackageIds.get(i + 1));
+ }
+ }
+
+ private List<Integer> getKnownPackageIdsList() throws IllegalAccessException {
+ final ArrayList<Integer> knownPackageIds = new ArrayList<>();
+ final Field[] allFields = PackageManagerInternal.class.getDeclaredFields();
+ for (Field field : allFields) {
+ final int modifier = field.getModifiers();
+ if (isPublic(modifier) && isStatic(modifier) && isFinal(modifier)
+ && Pattern.matches("PACKAGE(_[A-Z]+)+", field.getName())) {
+ knownPackageIds.add(field.getInt(null));
+ }
+ }
+ Collections.sort(knownPackageIds);
+ return knownPackageIds;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 3221a4d..59aff8d 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -20,12 +20,16 @@
import static org.junit.Assert.fail;
import android.content.Context;
+import android.hardware.power.stats.ChannelInfo;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyMeasurement;
import androidx.test.InstrumentationRegistry;
import com.android.server.SystemService;
import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
-import com.android.server.powerstats.nano.PowerStatsServiceProto;
+import com.android.server.powerstats.nano.PowerStatsServiceMeterProto;
+import com.android.server.powerstats.nano.PowerStatsServiceModelProto;
import org.junit.Before;
import org.junit.Test;
@@ -48,11 +52,12 @@
public class PowerStatsServiceTest {
private static final String TAG = PowerStatsServiceTest.class.getSimpleName();
private static final String DATA_STORAGE_SUBDIR = "powerstatstest";
- private static final String DATA_STORAGE_FILENAME = "test";
+ private static final String METER_FILENAME = "metertest";
+ private static final String MODEL_FILENAME = "modeltest";
private static final String PROTO_OUTPUT_FILENAME = "powerstats.proto";
- private static final String RAIL_NAME = "railname";
- private static final String SUBSYS_NAME = "subsysname";
- private static final int POWER_RAIL_COUNT = 8;
+ private static final String CHANNEL_NAME = "channelname";
+ private static final int ENERGY_METER_COUNT = 8;
+ private static final int ENERGY_CONSUMER_COUNT = 2;
private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
private PowerStatsService mService;
@@ -75,8 +80,13 @@
}
@Override
- String createDataStorageFilename() {
- return DATA_STORAGE_FILENAME;
+ String createMeterFilename() {
+ return METER_FILENAME;
+ }
+
+ @Override
+ String createModelFilename() {
+ return MODEL_FILENAME;
}
@Override
@@ -86,9 +96,10 @@
@Override
PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
- String dataStorageFilename, IPowerStatsHALWrapper powerStatsHALWrapper) {
- mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath, dataStorageFilename,
- powerStatsHALWrapper);
+ String meterFilename, String modelFilename,
+ IPowerStatsHALWrapper powerStatsHALWrapper) {
+ mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath, meterFilename,
+ modelFilename, powerStatsHALWrapper);
return mPowerStatsLogger;
}
@@ -107,23 +118,48 @@
public static final class TestPowerStatsHALWrapper implements IPowerStatsHALWrapper {
@Override
- public PowerStatsData.RailInfo[] readRailInfo() {
- PowerStatsData.RailInfo[] railInfoArray = new PowerStatsData.RailInfo[POWER_RAIL_COUNT];
- for (int i = 0; i < POWER_RAIL_COUNT; i++) {
- railInfoArray[i] = new PowerStatsData.RailInfo(i, RAIL_NAME + i, SUBSYS_NAME + i,
- i);
+ public int[] getEnergyConsumerInfo() {
+ int[] energyConsumerInfoList = new int[ENERGY_CONSUMER_COUNT];
+ for (int i = 0; i < energyConsumerInfoList.length; i++) {
+ energyConsumerInfoList[i] = i;
}
- return railInfoArray;
+ return energyConsumerInfoList;
}
@Override
- public PowerStatsData.EnergyData[] readEnergyData() {
- PowerStatsData.EnergyData[] energyDataArray =
- new PowerStatsData.EnergyData[POWER_RAIL_COUNT];
- for (int i = 0; i < POWER_RAIL_COUNT; i++) {
- energyDataArray[i] = new PowerStatsData.EnergyData(i, i, i);
+ public EnergyConsumerResult[] getEnergyConsumed() {
+ EnergyConsumerResult[] energyConsumedList =
+ new EnergyConsumerResult[ENERGY_CONSUMER_COUNT];
+ for (int i = 0; i < energyConsumedList.length; i++) {
+ energyConsumedList[i] = new EnergyConsumerResult();
+ energyConsumedList[i].energyConsumerId = i;
+ energyConsumedList[i].timestampMs = i;
+ energyConsumedList[i].energyUWs = i;
}
- return energyDataArray;
+ return energyConsumedList;
+ }
+
+ @Override
+ public ChannelInfo[] getEnergyMeterInfo() {
+ ChannelInfo[] energyMeterInfoList = new ChannelInfo[ENERGY_METER_COUNT];
+ for (int i = 0; i < energyMeterInfoList.length; i++) {
+ energyMeterInfoList[i] = new ChannelInfo();
+ energyMeterInfoList[i].channelId = i;
+ energyMeterInfoList[i].channelName = new String(CHANNEL_NAME + i);
+ }
+ return energyMeterInfoList;
+ }
+
+ @Override
+ public EnergyMeasurement[] readEnergyMeters() {
+ EnergyMeasurement[] energyMeasurementList = new EnergyMeasurement[ENERGY_METER_COUNT];
+ for (int i = 0; i < energyMeasurementList.length; i++) {
+ energyMeasurementList[i] = new EnergyMeasurement();
+ energyMeasurementList[i].channelId = i;
+ energyMeasurementList[i].timestampMs = i;
+ energyMeasurementList[i].energyUWs = i;
+ }
+ return energyMeasurementList;
}
@Override
@@ -138,7 +174,7 @@
}
@Test
- public void testWrittenPowerStatsHALDataMatchesReadIncidentReportData()
+ public void testWrittenMeterDataMatchesReadIncidentReportData()
throws InterruptedException, IOException {
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
@@ -152,36 +188,74 @@
// Write on-device storage to an incident report.
File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
FileOutputStream fos = new FileOutputStream(incidentReport);
- mPowerStatsLogger.writeToFile(fos.getFD());
+ mPowerStatsLogger.writeMeterDataToFile(fos.getFD());
// Read the incident report in to a byte array.
FileInputStream fis = new FileInputStream(incidentReport);
byte[] fileContent = new byte[(int) incidentReport.length()];
fis.read(fileContent);
- // Parse the incident data into a PowerStatsServiceProto object.
- PowerStatsServiceProto pssProto = PowerStatsServiceProto.parseFrom(fileContent);
+ // Parse the incident data into a PowerStatsServiceMeterProto object.
+ PowerStatsServiceMeterProto pssProto = PowerStatsServiceMeterProto.parseFrom(fileContent);
- // Validate the railInfo array matches what was written to on-device storage.
- assertTrue(pssProto.railInfo.length == POWER_RAIL_COUNT);
- for (int i = 0; i < pssProto.railInfo.length; i++) {
- assertTrue(pssProto.railInfo[i].index == i);
- assertTrue(pssProto.railInfo[i].railName.equals(RAIL_NAME + i));
- assertTrue(pssProto.railInfo[i].subsysName.equals(SUBSYS_NAME + i));
- assertTrue(pssProto.railInfo[i].samplingRate == i);
+ // Validate the channelInfo array matches what was written to on-device storage.
+ assertTrue(pssProto.channelInfo.length == ENERGY_METER_COUNT);
+ for (int i = 0; i < pssProto.channelInfo.length; i++) {
+ assertTrue(pssProto.channelInfo[i].channelId == i);
+ assertTrue(pssProto.channelInfo[i].channelName.equals(CHANNEL_NAME + i));
}
- // Validate the energyData array matches what was written to on-device storage.
- assertTrue(pssProto.energyData.length == POWER_RAIL_COUNT);
- for (int i = 0; i < pssProto.energyData.length; i++) {
- assertTrue(pssProto.energyData[i].index == i);
- assertTrue(pssProto.energyData[i].timestampMs == i);
- assertTrue(pssProto.energyData[i].energyUws == i);
+ // Validate the energyMeasurement array matches what was written to on-device storage.
+ assertTrue(pssProto.energyMeasurement.length == ENERGY_METER_COUNT);
+ for (int i = 0; i < pssProto.energyMeasurement.length; i++) {
+ assertTrue(pssProto.energyMeasurement[i].channelId == i);
+ assertTrue(pssProto.energyMeasurement[i].timestampMs == i);
+ assertTrue(pssProto.energyMeasurement[i].energyUws == i);
}
}
@Test
- public void testCorruptOnDeviceStorage() throws IOException {
+ public void testWrittenModelDataMatchesReadIncidentReportData()
+ throws InterruptedException, IOException {
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // Write data to on-device storage.
+ mTimerTrigger.logPowerStatsData();
+
+ // The above call puts a message on a handler. Wait for
+ // it to be processed.
+ Thread.sleep(100);
+
+ // Write on-device storage to an incident report.
+ File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
+ FileOutputStream fos = new FileOutputStream(incidentReport);
+ mPowerStatsLogger.writeModelDataToFile(fos.getFD());
+
+ // Read the incident report in to a byte array.
+ FileInputStream fis = new FileInputStream(incidentReport);
+ byte[] fileContent = new byte[(int) incidentReport.length()];
+ fis.read(fileContent);
+
+ // Parse the incident data into a PowerStatsServiceModelProto object.
+ PowerStatsServiceModelProto pssProto = PowerStatsServiceModelProto.parseFrom(fileContent);
+
+ // Validate the energyConsumerId array matches what was written to on-device storage.
+ assertTrue(pssProto.energyConsumerId.length == ENERGY_CONSUMER_COUNT);
+ for (int i = 0; i < pssProto.energyConsumerId.length; i++) {
+ assertTrue(pssProto.energyConsumerId[i].energyConsumerId == i);
+ }
+
+ // Validate the energyConsumerResult array matches what was written to on-device storage.
+ assertTrue(pssProto.energyConsumerResult.length == ENERGY_CONSUMER_COUNT);
+ for (int i = 0; i < pssProto.energyConsumerResult.length; i++) {
+ assertTrue(pssProto.energyConsumerResult[i].energyConsumerId == i);
+ assertTrue(pssProto.energyConsumerResult[i].timestampMs == i);
+ assertTrue(pssProto.energyConsumerResult[i].energyUws == i);
+ }
+ }
+
+ @Test
+ public void testCorruptOnDeviceMeterStorage() throws IOException {
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
// Generate random array of bytes to emulate corrupt data.
@@ -191,7 +265,7 @@
// Store corrupt data in on-device storage. Add fake timestamp to filename
// to match format expected by FileRotator.
- File onDeviceStorageFile = new File(mDataStorageDir, DATA_STORAGE_FILENAME + ".1234-2234");
+ File onDeviceStorageFile = new File(mDataStorageDir, METER_FILENAME + ".1234-2234");
FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
onDeviceStorageFos.write(bytes);
onDeviceStorageFos.close();
@@ -199,33 +273,72 @@
// Write on-device storage to an incident report.
File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
FileOutputStream incidentReportFos = new FileOutputStream(incidentReport);
- mPowerStatsLogger.writeToFile(incidentReportFos.getFD());
+ mPowerStatsLogger.writeMeterDataToFile(incidentReportFos.getFD());
// Read the incident report in to a byte array.
FileInputStream fis = new FileInputStream(incidentReport);
byte[] fileContent = new byte[(int) incidentReport.length()];
fis.read(fileContent);
- // Parse the incident data into a PowerStatsServiceProto object.
- PowerStatsServiceProto pssProto = PowerStatsServiceProto.parseFrom(fileContent);
+ // Parse the incident data into a PowerStatsServiceMeterProto object.
+ PowerStatsServiceMeterProto pssProto = PowerStatsServiceMeterProto.parseFrom(fileContent);
- // Valid railInfo data is written to the incident report in the call to
- // mPowerStatsLogger.writeToFile().
- assertTrue(pssProto.railInfo.length == POWER_RAIL_COUNT);
- for (int i = 0; i < pssProto.railInfo.length; i++) {
- assertTrue(pssProto.railInfo[i].index == i);
- assertTrue(pssProto.railInfo[i].railName.equals(RAIL_NAME + i));
- assertTrue(pssProto.railInfo[i].subsysName.equals(SUBSYS_NAME + i));
- assertTrue(pssProto.railInfo[i].samplingRate == i);
+ // Valid channelInfo data is written to the incident report in the call to
+ // mPowerStatsLogger.writeMeterDataToFile().
+ assertTrue(pssProto.channelInfo.length == ENERGY_METER_COUNT);
+ for (int i = 0; i < pssProto.channelInfo.length; i++) {
+ assertTrue(pssProto.channelInfo[i].channelId == i);
+ assertTrue(pssProto.channelInfo[i].channelName.equals(CHANNEL_NAME + i));
}
- // No energyData should be written to the incident report since it
+ // No energyMeasurements should be written to the incident report since it
// is all corrupt (random bytes generated above).
- assertTrue(pssProto.energyData.length == 0);
+ assertTrue(pssProto.energyMeasurement.length == 0);
}
@Test
- public void testNotEnoughBytesAfterLengthField() throws IOException {
+ public void testCorruptOnDeviceModelStorage() throws IOException {
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // Generate random array of bytes to emulate corrupt data.
+ Random rd = new Random();
+ byte[] bytes = new byte[100];
+ rd.nextBytes(bytes);
+
+ // Store corrupt data in on-device storage. Add fake timestamp to filename
+ // to match format expected by FileRotator.
+ File onDeviceStorageFile = new File(mDataStorageDir, MODEL_FILENAME + ".1234-2234");
+ FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(bytes);
+ onDeviceStorageFos.close();
+
+ // Write on-device storage to an incident report.
+ File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
+ FileOutputStream incidentReportFos = new FileOutputStream(incidentReport);
+ mPowerStatsLogger.writeModelDataToFile(incidentReportFos.getFD());
+
+ // Read the incident report in to a byte array.
+ FileInputStream fis = new FileInputStream(incidentReport);
+ byte[] fileContent = new byte[(int) incidentReport.length()];
+ fis.read(fileContent);
+
+ // Parse the incident data into a PowerStatsServiceModelProto object.
+ PowerStatsServiceModelProto pssProto = PowerStatsServiceModelProto.parseFrom(fileContent);
+
+ // Valid energyConsumerId data is written to the incident report in the call to
+ // mPowerStatsLogger.writeModelDataToFile().
+ assertTrue(pssProto.energyConsumerId.length == ENERGY_CONSUMER_COUNT);
+ for (int i = 0; i < pssProto.energyConsumerId.length; i++) {
+ assertTrue(pssProto.energyConsumerId[i].energyConsumerId == i);
+ }
+
+ // No energyConsumerResults should be written to the incident report since it
+ // is all corrupt (random bytes generated above).
+ assertTrue(pssProto.energyConsumerResult.length == 0);
+ }
+
+ @Test
+ public void testNotEnoughBytesAfterMeterLengthField() throws IOException {
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
// Create corrupt data.
@@ -236,7 +349,7 @@
// Store corrupt data in on-device storage. Add fake timestamp to filename
// to match format expected by FileRotator.
- File onDeviceStorageFile = new File(mDataStorageDir, DATA_STORAGE_FILENAME + ".1234-2234");
+ File onDeviceStorageFile = new File(mDataStorageDir, METER_FILENAME + ".1234-2234");
FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
onDeviceStorageFos.write(data.toByteArray());
onDeviceStorageFos.close();
@@ -244,28 +357,68 @@
// Write on-device storage to an incident report.
File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
FileOutputStream incidentReportFos = new FileOutputStream(incidentReport);
- mPowerStatsLogger.writeToFile(incidentReportFos.getFD());
+ mPowerStatsLogger.writeMeterDataToFile(incidentReportFos.getFD());
// Read the incident report in to a byte array.
FileInputStream fis = new FileInputStream(incidentReport);
byte[] fileContent = new byte[(int) incidentReport.length()];
fis.read(fileContent);
- // Parse the incident data into a PowerStatsServiceProto object.
- PowerStatsServiceProto pssProto = PowerStatsServiceProto.parseFrom(fileContent);
+ // Parse the incident data into a PowerStatsServiceMeterProto object.
+ PowerStatsServiceMeterProto pssProto = PowerStatsServiceMeterProto.parseFrom(fileContent);
- // Valid railInfo data is written to the incident report in the call to
- // mPowerStatsLogger.writeToFile().
- assertTrue(pssProto.railInfo.length == POWER_RAIL_COUNT);
- for (int i = 0; i < pssProto.railInfo.length; i++) {
- assertTrue(pssProto.railInfo[i].index == i);
- assertTrue(pssProto.railInfo[i].railName.equals(RAIL_NAME + i));
- assertTrue(pssProto.railInfo[i].subsysName.equals(SUBSYS_NAME + i));
- assertTrue(pssProto.railInfo[i].samplingRate == i);
+ // Valid channelInfo data is written to the incident report in the call to
+ // mPowerStatsLogger.writeMeterDataToFile().
+ assertTrue(pssProto.channelInfo.length == ENERGY_METER_COUNT);
+ for (int i = 0; i < pssProto.channelInfo.length; i++) {
+ assertTrue(pssProto.channelInfo[i].channelId == i);
+ assertTrue(pssProto.channelInfo[i].channelName.equals(CHANNEL_NAME + i));
}
- // No energyData should be written to the incident report since the
+ // No energyMeasurements should be written to the incident report since the
// input buffer had only length and no data.
- assertTrue(pssProto.energyData.length == 0);
+ assertTrue(pssProto.energyMeasurement.length == 0);
+ }
+
+ @Test
+ public void testNotEnoughBytesAfterModelLengthField() throws IOException {
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ // Create corrupt data.
+ // Length field is correct, but there is no data following the length.
+ ByteArrayOutputStream data = new ByteArrayOutputStream();
+ data.write(ByteBuffer.allocate(4).putInt(50).array());
+ byte[] test = data.toByteArray();
+
+ // Store corrupt data in on-device storage. Add fake timestamp to filename
+ // to match format expected by FileRotator.
+ File onDeviceStorageFile = new File(mDataStorageDir, MODEL_FILENAME + ".1234-2234");
+ FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile);
+ onDeviceStorageFos.write(data.toByteArray());
+ onDeviceStorageFos.close();
+
+ // Write on-device storage to an incident report.
+ File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME);
+ FileOutputStream incidentReportFos = new FileOutputStream(incidentReport);
+ mPowerStatsLogger.writeModelDataToFile(incidentReportFos.getFD());
+
+ // Read the incident report in to a byte array.
+ FileInputStream fis = new FileInputStream(incidentReport);
+ byte[] fileContent = new byte[(int) incidentReport.length()];
+ fis.read(fileContent);
+
+ // Parse the incident data into a PowerStatsServiceModelProto object.
+ PowerStatsServiceModelProto pssProto = PowerStatsServiceModelProto.parseFrom(fileContent);
+
+ // Valid energyConsumerId data is written to the incident report in the call to
+ // mPowerStatsLogger.writeModelDataToFile().
+ assertTrue(pssProto.energyConsumerId.length == ENERGY_CONSUMER_COUNT);
+ for (int i = 0; i < pssProto.energyConsumerId.length; i++) {
+ assertTrue(pssProto.energyConsumerId[i].energyConsumerId == i);
+ }
+
+ // No energyConsumerResults should be written to the incident report since the
+ // input buffer had only length and no data.
+ assertTrue(pssProto.energyConsumerResult.length == 0);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index 8cba69f..3530e38 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -34,7 +34,6 @@
List<Integer> mUsers = new ArrayList<>();
// Package -> [user, package]
Map<String, Map<Integer, PackageInfo>> mPackages = new HashMap();
- private boolean mFallbackLogicEnabled;
private final int mNumRelros;
private final boolean mIsDebuggable;
private int mMultiProcessSetting;
@@ -42,10 +41,9 @@
public static final int PRIMARY_USER_ID = 0;
- public TestSystemImpl(WebViewProviderInfo[] packageConfigs, boolean fallbackLogicEnabled,
- int numRelros, boolean isDebuggable, boolean multiProcessDefault) {
+ public TestSystemImpl(WebViewProviderInfo[] packageConfigs, int numRelros, boolean isDebuggable,
+ boolean multiProcessDefault) {
mPackageConfigs = packageConfigs;
- mFallbackLogicEnabled = fallbackLogicEnabled;
mNumRelros = numRelros;
mIsDebuggable = isDebuggable;
mUsers.add(PRIMARY_USER_ID);
@@ -78,16 +76,6 @@
public void killPackageDependents(String packageName) {}
@Override
- public boolean isFallbackLogicEnabled() {
- return mFallbackLogicEnabled;
- }
-
- @Override
- public void enableFallbackLogic(boolean enable) {
- mFallbackLogicEnabled = enable;
- }
-
- @Override
public void enablePackageForAllUsers(Context context, String packageName, boolean enable) {
for(int userId : mUsers) {
enablePackageForUser(packageName, enable, userId);
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index bbfc5ab..ebe45a6 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -67,36 +67,30 @@
}
private void setupWithPackages(WebViewProviderInfo[] packages) {
- setupWithAllParameters(packages, false /* fallbackLogicEnabled */, 1 /* numRelros */,
- true /* isDebuggable */, false /* multiProcessDefault */);
- }
-
- private void setupWithPackagesAndFallbackLogic(WebViewProviderInfo[] packages) {
- setupWithAllParameters(packages, true /* fallbackLogicEnabled */, 1 /* numRelros */,
- true /* isDebuggable */, false /* multiProcessDefault */);
+ setupWithAllParameters(packages, 1 /* numRelros */, true /* isDebuggable */,
+ false /* multiProcessDefault */);
}
private void setupWithPackagesAndRelroCount(WebViewProviderInfo[] packages, int numRelros) {
- setupWithAllParameters(packages, false /* fallbackLogicEnabled */, numRelros,
- true /* isDebuggable */, false /* multiProcessDefault */);
+ setupWithAllParameters(packages, numRelros, true /* isDebuggable */,
+ false /* multiProcessDefault */);
}
private void setupWithPackagesNonDebuggable(WebViewProviderInfo[] packages) {
- setupWithAllParameters(packages, false /* fallbackLogicEnabled */, 1 /* numRelros */,
- false /* isDebuggable */, false /* multiProcessDefault */);
+ setupWithAllParameters(packages, 1 /* numRelros */, false /* isDebuggable */,
+ false /* multiProcessDefault */);
}
private void setupWithPackagesAndMultiProcess(WebViewProviderInfo[] packages,
boolean multiProcessDefault) {
- setupWithAllParameters(packages, false /* fallbackLogicEnabled */, 1 /* numRelros */,
- true /* isDebuggable */, multiProcessDefault);
+ setupWithAllParameters(packages, 1 /* numRelros */, true /* isDebuggable */,
+ multiProcessDefault);
}
- private void setupWithAllParameters(WebViewProviderInfo[] packages,
- boolean fallbackLogicEnabled, int numRelros, boolean isDebuggable,
- boolean multiProcessDefault) {
- TestSystemImpl testing = new TestSystemImpl(packages, fallbackLogicEnabled, numRelros,
- isDebuggable, multiProcessDefault);
+ private void setupWithAllParameters(WebViewProviderInfo[] packages, int numRelros,
+ boolean isDebuggable, boolean multiProcessDefault) {
+ TestSystemImpl testing = new TestSystemImpl(packages, numRelros, isDebuggable,
+ multiProcessDefault);
mTestSystemImpl = Mockito.spy(testing);
mWebViewUpdateServiceImpl =
new WebViewUpdateServiceImpl(null /*Context*/, mTestSystemImpl);
@@ -514,49 +508,29 @@
}
/**
- * Scenario for testing migrating away from the fallback logic.
- * We start with a primary package that's a disabled fallback, and an enabled secondary,
- * so that the fallback being re-enabled will cause a provider switch, as that covers
- * the most complex case.
+ * Scenario for testing re-enabling a fallback package.
*/
@Test
- public void testFallbackLogicMigration() {
- String primaryPackage = "primary";
- String secondaryPackage = "secondary";
+ public void testFallbackPackageEnabling() {
+ String testPackage = "testFallback";
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
new WebViewProviderInfo(
- primaryPackage, "", true /* default available */, true /* fallback */, null),
- new WebViewProviderInfo(
- secondaryPackage, "", true /* default available */, false /* fallback */,
- null)};
- setupWithPackagesAndFallbackLogic(packages);
+ testPackage, "", true /* default available */, true /* fallback */, null)};
+ setupWithPackages(packages);
mTestSystemImpl.setPackageInfo(
- createPackageInfo(primaryPackage, false /* enabled */ , true /* valid */,
- true /* installed */));
- mTestSystemImpl.setPackageInfo(
- createPackageInfo(secondaryPackage, true /* enabled */ , true /* valid */,
+ createPackageInfo(testPackage, false /* enabled */ , true /* valid */,
true /* installed */));
- // Check that the boot time logic re-enables and chooses the primary, and disables the
- // fallback logic.
+ // Check that the boot time logic re-enables the fallback package.
runWebViewBootPreparationOnMainSync();
Mockito.verify(mTestSystemImpl).enablePackageForAllUsers(
- Matchers.anyObject(), Mockito.eq(primaryPackage), Mockito.eq(true));
- checkPreparationPhasesForPackage(primaryPackage, 1);
- assertFalse(mTestSystemImpl.isFallbackLogicEnabled());
+ Matchers.anyObject(), Mockito.eq(testPackage), Mockito.eq(true));
- // Disable primary again
- mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, false /* enabled */,
- true /* valid */, true /* installed */));
- mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
- WebViewUpdateService.PACKAGE_CHANGED, TestSystemImpl.PRIMARY_USER_ID);
- checkPreparationPhasesForPackage(secondaryPackage, 1);
-
- // Run boot logic again and check that we didn't re-enable the primary a second time.
- runWebViewBootPreparationOnMainSync();
- Mockito.verify(mTestSystemImpl, Mockito.times(1)).enablePackageForAllUsers(
- Matchers.anyObject(), Mockito.eq(primaryPackage), Mockito.eq(true));
- checkPreparationPhasesForPackage(secondaryPackage, 2);
+ // Fake the message about the enabling having changed the package state,
+ // and check we now use that package.
+ mWebViewUpdateServiceImpl.packageStateChanged(
+ testPackage, WebViewUpdateService.PACKAGE_CHANGED, TestSystemImpl.PRIMARY_USER_ID);
+ checkPreparationPhasesForPackage(testPackage, 1);
}
/**
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 1100496..740505e 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -6691,7 +6691,7 @@
orig)));
mBinderService.createConversationNotificationChannelForPackage(
- PKG, mUid, "key", orig, "friend");
+ PKG, mUid, orig, "friend");
NotificationChannel friendChannel = mBinderService.getConversationNotificationChannel(
PKG, 0, PKG, original.getId(), false, "friend");
@@ -6726,10 +6726,10 @@
String conversationId = "friend";
mBinderService.createConversationNotificationChannelForPackage(
- PKG, mUid, "key", NotificationChannel.CREATOR.createFromParcel(msgParcel),
+ PKG, mUid, NotificationChannel.CREATOR.createFromParcel(msgParcel),
conversationId);
mBinderService.createConversationNotificationChannelForPackage(
- PKG, mUid, "key", NotificationChannel.CREATOR.createFromParcel(callParcel),
+ PKG, mUid, NotificationChannel.CREATOR.createFromParcel(callParcel),
conversationId);
NotificationChannel messagesChild = mBinderService.getConversationNotificationChannel(
@@ -7145,7 +7145,8 @@
inOrder.verify(child).recordDismissalSentiment(anyInt());
}
- @Test
+ // TODO (b/171418004): renable after app outreach
+ /*@Test
public void testImmutableBubbleIntent() throws Exception {
when(mAmi.getPendingIntentFlags(pi1))
.thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
@@ -7160,7 +7161,7 @@
} catch (IllegalArgumentException e) {
// good
}
- }
+ }*/
@Test
public void testMutableBubbleIntent() throws Exception {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 9be3164..f1d3e18 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -35,6 +35,7 @@
import android.app.ActivityOptions;
import android.app.ActivityOptions.SourceInfo;
+import android.app.WaitResult;
import android.content.Intent;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
@@ -167,10 +168,15 @@
@Test
public void testOnActivityLaunchFinished() {
+ // Assume that the process is started (ActivityBuilder has mocked the returned value of
+ // ATMS#getProcessController) but the activity has not attached process.
+ mTopActivity.app = null;
onActivityLaunched(mTopActivity);
notifyTransitionStarting(mTopActivity);
- notifyWindowsDrawn(mTopActivity);
+ final ActivityMetricsLogger.TransitionInfoSnapshot info = notifyWindowsDrawn(mTopActivity);
+ assertWithMessage("Warm launch").that(info.getLaunchState())
+ .isEqualTo(WaitResult.LAUNCH_STATE_WARM);
verifyOnActivityLaunchFinished(mTopActivity);
verifyNoMoreInteractions(mLaunchObserver);
@@ -204,7 +210,7 @@
notifyActivityLaunching(noDrawnActivity.intent);
notifyActivityLaunched(START_SUCCESS, noDrawnActivity);
- noDrawnActivity.destroyIfPossible("test");
+ noDrawnActivity.mVisibleRequested = false;
mActivityMetricsLogger.notifyVisibilityChanged(noDrawnActivity);
verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(noDrawnActivity));
@@ -225,6 +231,8 @@
assertWithMessage("Record start source").that(info.sourceType)
.isEqualTo(SourceInfo.TYPE_LAUNCHER);
assertWithMessage("Record event time").that(info.sourceEventDelayMs).isAtLeast(10);
+ assertWithMessage("Hot launch").that(info.getLaunchState())
+ .isEqualTo(WaitResult.LAUNCH_STATE_HOT);
verifyAsync(mLaunchObserver).onReportFullyDrawn(eqProto(mTopActivity), anyLong());
verifyOnActivityLaunchFinished(mTopActivity);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index dd4d718..f378345 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -32,7 +32,7 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.os.Process.NOBODY_UID;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
@@ -114,6 +114,7 @@
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
+
/**
* Tests for the {@link ActivityRecord} class.
*
@@ -844,7 +845,8 @@
assertEquals(PAUSING, mActivity.getState());
verify(mActivity).setVisibility(eq(false));
verify(mActivity.mDisplayContent)
- .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
+ .prepareAppTransitionOld(eq(TRANSIT_OLD_TASK_CLOSE),
+ eq(false) /* alwaysKeepCurrent */);
}
/**
@@ -888,7 +890,8 @@
verify(mActivity).setVisibility(eq(false));
verify(mActivity.mDisplayContent)
- .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
+ .prepareAppTransitionOld(eq(TRANSIT_OLD_TASK_CLOSE),
+ eq(false) /* alwaysKeepCurrent */);
verify(mActivity.mDisplayContent, never()).executeAppTransition();
}
@@ -904,7 +907,8 @@
verify(mActivity, atLeast(1)).setVisibility(eq(false));
verify(mActivity.mDisplayContent)
- .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
+ .prepareAppTransitionOld(eq(TRANSIT_OLD_TASK_CLOSE),
+ eq(false) /* alwaysKeepCurrent */);
verify(mActivity.mDisplayContent).executeAppTransition();
}
@@ -922,7 +926,8 @@
mActivity.finishIfPossible("test", false /* oomAdj */);
verify(mActivity.mDisplayContent, never())
- .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
+ .prepareAppTransitionOld(eq(TRANSIT_OLD_TASK_CLOSE),
+ eq(false) /* alwaysKeepCurrent */);
}
/**
@@ -1668,7 +1673,7 @@
}
@Test
- public void testCanTurnScreenOn() {
+ public void testFullscreenWindowCanTurnScreenOn() {
mStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
doReturn(true).when(mActivity).getTurnScreenOnFlag();
@@ -1676,11 +1681,11 @@
}
@Test
- public void testFreeformWindowCantTurnScreenOn() {
+ public void testFreeformWindowCanTurnScreenOn() {
mStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
doReturn(true).when(mActivity).getTurnScreenOnFlag();
- assertFalse(mActivity.canTurnScreenOn());
+ assertTrue(mActivity.canTurnScreenOn());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 3dc258c..b632331 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -70,8 +70,6 @@
import android.os.Binder;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
-import android.view.SurfaceControl;
-import android.window.ITaskOrganizer;
import androidx.test.filters.SmallTest;
@@ -242,32 +240,13 @@
@Test
public void testRemoveOrganizedTask_UpdateStackReference() {
- ITaskOrganizer listener = new ITaskOrganizer.Stub() {
- @Override
- public void onTaskAppeared(
- ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { }
-
- @Override
- public void onTaskVanished(ActivityManager.RunningTaskInfo container) { }
-
- @Override
- public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
- }
-
- @Override
- public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
- }
- };
- mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
-
final Task rootHomeTask = mDefaultTaskDisplayArea.getRootHomeTask();
final ActivityRecord homeActivity = new ActivityBuilder(mAtm)
.setStack(rootHomeTask)
.setCreateTask(true)
.build();
- final Task secondaryStack = (Task) WindowContainer.fromBinder(
- mAtm.mTaskOrganizerController.createRootTask(rootHomeTask.getDisplayId(),
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).getTaskInfo().token.asBinder());
+ final Task secondaryStack = mAtm.mTaskOrganizerController.createRootTask(
+ rootHomeTask.getDisplayContent(), WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
rootHomeTask.reparent(secondaryStack, POSITION_TOP);
assertEquals(secondaryStack, rootHomeTask.getParent());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index e478819..3720e520 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1022,7 +1022,7 @@
assertThat(outActivity[0].inSplitScreenWindowingMode()).isFalse();
// Move activity to split-screen-primary stack and make sure it has the focus.
- TestSplitOrganizer splitOrg = new TestSplitOrganizer(mAtm, top.getDisplayId());
+ TestSplitOrganizer splitOrg = new TestSplitOrganizer(mAtm, top.getDisplayContent());
top.getRootTask().reparent(splitOrg.mPrimary, POSITION_BOTTOM);
top.getRootTask().moveToFront("testWindowingModeOptionsLaunchAdjacent");
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 8292420..3d31824 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -54,6 +54,7 @@
import org.mockito.MockitoSession;
import java.util.ArrayList;
+import java.util.function.Consumer;
/**
* Tests for the {@link ActivityTaskManagerService} class.
@@ -261,15 +262,17 @@
public void testUpdateSleep() {
doCallRealMethod().when(mWm.mRoot).hasAwakeDisplay();
mSupervisor.mGoingToSleepWakeLock = mock(PowerManager.WakeLock.class);
+ final ActivityRecord homeActivity = new ActivityBuilder(mAtm)
+ .setTask(mWm.mRoot.getDefaultTaskDisplayArea().getOrCreateRootHomeTask()).build();
final ActivityRecord topActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
topActivity.setState(Task.ActivityState.RESUMED, "test");
- final Runnable assertTopNonSleeping = () -> {
+ final Consumer<ActivityRecord> assertTopNonSleeping = activity -> {
assertFalse(mAtm.mInternal.isSleeping());
assertEquals(ActivityManager.PROCESS_STATE_TOP, mAtm.mInternal.getTopProcessState());
- assertEquals(topActivity.app, mAtm.mInternal.getTopApp());
+ assertEquals(activity.app, mAtm.mInternal.getTopApp());
};
- assertTopNonSleeping.run();
+ assertTopNonSleeping.accept(topActivity);
// Sleep all displays.
mWm.mRoot.forAllDisplays(display -> doReturn(true).when(display).shouldSleep());
@@ -279,13 +282,18 @@
assertTrue(mAtm.mInternal.isSleeping());
assertEquals(ActivityManager.PROCESS_STATE_TOP_SLEEPING,
mAtm.mInternal.getTopProcessState());
- assertNull(mAtm.mInternal.getTopApp());
+ // The top app should not change while sleeping.
+ assertEquals(topActivity.app, mAtm.mInternal.getTopApp());
+
+ // Move the current top to back, the top app should update to the next activity.
+ topActivity.getRootTask().moveToBack("test", null /* self */);
+ assertEquals(homeActivity.app, mAtm.mInternal.getTopApp());
// Wake all displays.
mWm.mRoot.forAllDisplays(display -> doReturn(false).when(display).shouldSleep());
mAtm.updateSleepIfNeededLocked();
- assertTopNonSleeping.run();
+ assertTopNonSleeping.accept(homeActivity);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 673feb2..30502d8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -20,7 +20,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -66,7 +66,7 @@
RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
RemoteAnimationAdapter adapter =
new RemoteAnimationAdapter(new TestRemoteAnimationRunner(), 10, 1, false);
- definition.addRemoteAnimation(TRANSIT_TASK_CHANGE_WINDOWING_MODE, adapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE, adapter);
dc.registerRemoteAnimations(definition);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 1b21920..c1212f5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -20,10 +20,10 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
-import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -76,8 +76,9 @@
translucentOpening.setVisible(false);
mDisplayContent.mOpeningApps.add(behind);
mDisplayContent.mOpeningApps.add(translucentOpening);
- assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
- mAppTransitionController.maybeUpdateTransitToTranslucentAnim(TRANSIT_TASK_OPEN));
+ assertEquals(WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN,
+ mAppTransitionController.maybeUpdateTransitToTranslucentAnim(
+ TRANSIT_OLD_TASK_OPEN));
}
@Test
@@ -89,8 +90,9 @@
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
translucentClosing.setOccludesParent(false);
mDisplayContent.mClosingApps.add(translucentClosing);
- assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE,
- mAppTransitionController.maybeUpdateTransitToTranslucentAnim(TRANSIT_TASK_CLOSE));
+ assertEquals(WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE,
+ mAppTransitionController.maybeUpdateTransitToTranslucentAnim(
+ TRANSIT_OLD_TASK_CLOSE));
}
@Test
@@ -104,9 +106,9 @@
translucentOpening.setVisible(false);
mDisplayContent.mOpeningApps.add(behind);
mDisplayContent.mOpeningApps.add(translucentOpening);
- assertEquals(TRANSIT_TASK_CHANGE_WINDOWING_MODE,
+ assertEquals(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE,
mAppTransitionController.maybeUpdateTransitToTranslucentAnim(
- TRANSIT_TASK_CHANGE_WINDOWING_MODE));
+ TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE));
}
@Test
@@ -121,11 +123,11 @@
final Task task = opening.getTask();
mDisplayContent.mOpeningApps.add(opening);
mDisplayContent.mClosingApps.add(closing);
- assertFalse(mAppTransitionController.isTransitWithinTask(TRANSIT_ACTIVITY_OPEN, task));
+ assertFalse(mAppTransitionController.isTransitWithinTask(TRANSIT_OLD_ACTIVITY_OPEN, task));
closing.getTask().removeChild(closing);
task.addChild(closing, 0);
- assertTrue(mAppTransitionController.isTransitWithinTask(TRANSIT_ACTIVITY_OPEN, task));
- assertFalse(mAppTransitionController.isTransitWithinTask(TRANSIT_TASK_OPEN, task));
+ assertTrue(mAppTransitionController.isTransitWithinTask(TRANSIT_OLD_ACTIVITY_OPEN, task));
+ assertFalse(mAppTransitionController.isTransitWithinTask(TRANSIT_OLD_TASK_OPEN, task));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index ee030af..485f92f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -20,11 +20,11 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -73,38 +73,40 @@
@Test
public void testKeyguardOverride() {
- mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
- mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
- assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransition());
+ mDc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
+ mDc.prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
+ assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransitionOld());
}
@Test
public void testKeyguardKeep() {
- mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
- mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
- assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransition());
+ mDc.prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
+ mDc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
+ assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransitionOld());
}
@Test
public void testForceOverride() {
- mWm.prepareAppTransition(TRANSIT_KEYGUARD_UNOCCLUDE, false /* alwaysKeepCurrent */);
- mDc.prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
+ mDc.prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_UNOCCLUDE, false /* alwaysKeepCurrent */);
+ mDc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN,
false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */);
- assertEquals(TRANSIT_ACTIVITY_OPEN, mDc.mAppTransition.getAppTransition());
+ assertEquals(TRANSIT_OLD_ACTIVITY_OPEN, mDc.mAppTransition.getAppTransitionOld());
}
@Test
public void testCrashing() {
- mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
- mWm.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
- assertEquals(TRANSIT_CRASHING_ACTIVITY_CLOSE, mDc.mAppTransition.getAppTransition());
+ mDc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
+ mDc.prepareAppTransitionOld(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE,
+ false /* alwaysKeepCurrent */);
+ assertEquals(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE, mDc.mAppTransition.getAppTransitionOld());
}
@Test
public void testKeepKeyguard_withCrashing() {
- mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
- mWm.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
- assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransition());
+ mDc.prepareAppTransitionOld(TRANSIT_OLD_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
+ mDc.prepareAppTransitionOld(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE,
+ false /* alwaysKeepCurrent */);
+ assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransitionOld());
}
@Test
@@ -125,12 +127,12 @@
// Simulate activity resume / finish flows to prepare app transition & set visibility,
// make sure transition is set as expected for each display.
- dc1.prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
+ dc1.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN,
false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
- assertEquals(TRANSIT_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransition());
- dc2.prepareAppTransition(TRANSIT_ACTIVITY_CLOSE,
+ assertEquals(TRANSIT_OLD_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransitionOld());
+ dc2.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_CLOSE,
false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
- assertEquals(TRANSIT_ACTIVITY_CLOSE, dc2.mAppTransition.getAppTransition());
+ assertEquals(TRANSIT_OLD_ACTIVITY_CLOSE, dc2.mAppTransition.getAppTransitionOld());
// One activity window is visible for resuming & the other activity window is invisible
// for finishing in different display.
activity1.setVisibility(true, false);
@@ -158,9 +160,9 @@
dc1.mClosingApps.add(activity1);
assertTrue(dc1.mClosingApps.size() > 0);
- dc1.prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
+ dc1.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_OPEN,
false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
- assertEquals(TRANSIT_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransition());
+ assertEquals(TRANSIT_OLD_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransitionOld());
assertTrue(dc1.mAppTransition.isTransitionSet());
dc1.mOpeningApps.add(activity1);
@@ -201,9 +203,9 @@
// Simulate activity finish flows to prepare app transition & set visibility,
// make sure transition is set as expected.
- dc.prepareAppTransition(TRANSIT_ACTIVITY_CLOSE,
+ dc.prepareAppTransitionOld(TRANSIT_OLD_ACTIVITY_CLOSE,
false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
- assertEquals(TRANSIT_ACTIVITY_CLOSE, dc.mAppTransition.getAppTransition());
+ assertEquals(TRANSIT_OLD_ACTIVITY_CLOSE, dc.mAppTransition.getAppTransitionOld());
dc.mAppTransition.overridePendingAppTransitionRemote(adapter);
exitingActivity.setVisibility(false, false);
assertTrue(dc.mClosingApps.size() > 0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 085b8de..6837e0e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -32,7 +32,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -414,7 +414,7 @@
sources.add(activity2);
doReturn(true).when(activity2).okToAnimate();
doReturn(true).when(activity2).isAnimating();
- assertTrue(activity2.applyAnimation(null, TRANSIT_ACTIVITY_OPEN, true, false, sources));
+ assertTrue(activity2.applyAnimation(null, TRANSIT_OLD_ACTIVITY_OPEN, true, false, sources));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 4d0d3b2..c9e56fd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1169,7 +1169,7 @@
final ActivityRecord app = mAppWindow.mActivityRecord;
app.setVisible(false);
- mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_OPEN,
+ mDisplayContent.prepareAppTransitionOld(WindowManager.TRANSIT_OLD_ACTIVITY_OPEN,
false /* alwaysKeepCurrent */);
mDisplayContent.mOpeningApps.add(app);
final int newOrientation = getRotatedOrientation(mDisplayContent);
@@ -1355,7 +1355,7 @@
final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
app.setVisible(false);
app.setState(Task.ActivityState.RESUMED, "test");
- mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_OPEN,
+ mDisplayContent.prepareAppTransitionOld(WindowManager.TRANSIT_OLD_ACTIVITY_OPEN,
false /* alwaysKeepCurrent */);
mDisplayContent.mOpeningApps.add(app);
final int newOrientation = getRotatedOrientation(mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 9b2a2db..e1aca55 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -664,20 +664,20 @@
// Non-rotation API Tests
// ========================
@Test
- public void testRespectsAppRequestedOrientationByDefault() throws Exception {
+ public void testIsNotFixedToUserRotationByDefault() throws Exception {
mBuilder.build();
- assertTrue("Display rotation should respect app requested orientation by"
- + " default.", mTarget.respectAppRequestedOrientation());
+ assertFalse("Display rotation should respect app requested orientation by"
+ + " default.", mTarget.isFixedToUserRotation());
}
@Test
- public void testNotRespectAppRequestedOrientation_FixedToUserRotation() throws Exception {
+ public void testIsFixedToUserRotation() throws Exception {
mBuilder.build();
mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
- assertFalse("Display rotation shouldn't respect app requested orientation if"
- + " fixed to user rotation.", mTarget.respectAppRequestedOrientation());
+ assertTrue("Display rotation shouldn't respect app requested orientation if"
+ + " fixed to user rotation.", mTarget.isFixedToUserRotation());
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 4536997..747fb89 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -18,27 +18,45 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
+import static android.view.DragEvent.ACTION_DRAG_STARTED;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.verify;
+import android.app.PendingIntent;
import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.Intent;
import android.graphics.PixelFormat;
+import android.os.Binder;
import android.os.IBinder;
import android.os.Looper;
+import android.os.Parcelable;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.platform.test.annotations.Presubmit;
+import android.view.DragEvent;
+import android.view.IWindowSessionCallback;
import android.view.InputChannel;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
+import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -51,6 +69,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -65,12 +84,15 @@
@RunWith(WindowTestRunner.class)
public class DragDropControllerTests extends WindowTestsBase {
private static final int TIMEOUT_MS = 3000;
+ private static final int TEST_UID = 12345;
+
private TestDragDropController mTarget;
private WindowState mWindow;
private IBinder mToken;
static class TestDragDropController extends DragDropController {
private Runnable mCloseCallback;
+ boolean mDeferDragStateClosed;
TestDragDropController(WindowManagerService service, Looper looper) {
super(service, looper);
@@ -83,6 +105,9 @@
@Override
void onDragStateClosedLocked(DragState dragState) {
+ if (mDeferDragStateClosed) {
+ return;
+ }
super.onDragStateClosedLocked(dragState);
if (mCloseCallback != null) {
mCloseCallback.run();
@@ -101,8 +126,9 @@
final Task task = createTaskInStack(stack, ownerId);
task.addChild(activity, 0);
+ // Use a new TestIWindow so we don't collect events for other windows
final WindowState window = createWindow(
- null, TYPE_BASE_APPLICATION, activity, name, ownerId, false);
+ null, TYPE_BASE_APPLICATION, activity, name, ownerId, false, new TestIWindow());
window.mInputChannel = new InputChannel();
window.mHasSurface = true;
return window;
@@ -146,12 +172,12 @@
@Test
public void testDragFlow() {
- dragFlow(0, ClipData.newPlainText("label", "Test"), 0, 0);
+ doDragAndDrop(0, ClipData.newPlainText("label", "Test"), 0, 0);
}
@Test
public void testPerformDrag_NullDataWithGrantUri() {
- dragFlow(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ, null, 0, 0);
+ doDragAndDrop(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ, null, 0, 0);
}
@Test
@@ -160,10 +186,146 @@
createDropTargetWindow("Other user's window", 1 * UserHandle.PER_USER_RANGE);
doReturn(otherUsersWindow).when(mDisplayContent).getTouchableWinAtPointLocked(10, 10);
- dragFlow(0, null, 10, 10);
+ doDragAndDrop(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ, null, 10, 10);
+ mToken = otherUsersWindow.mClient.asBinder();
}
- private void dragFlow(int flag, ClipData data, float dropX, float dropY) {
+ @Test
+ public void testPrivateInterceptGlobalDragDropFlagChecksPermission() {
+ spyOn(mWm.mContext);
+
+ DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
+ WindowManager.LayoutParams attrs = new WindowManager.LayoutParams();
+ attrs.privateFlags |= PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
+ policy.validateAddingWindowLw(attrs, Binder.getCallingPid(), Binder.getCallingUid());
+
+ verify(mWm.mContext).enforcePermission(
+ eq(android.Manifest.permission.MANAGE_ACTIVITY_STACKS), anyInt(), anyInt(), any());
+ }
+
+ @Test
+ public void testPrivateInterceptGlobalDragDropFlagBehaviour() {
+ mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
+ mWindow.setViewVisibility(View.GONE);
+
+ // Necessary for now since DragState.sendDragStartedLocked() will recycle drag events
+ // immediately after dispatching, which is a problem when using mockito arguments captor
+ // because it returns and modifies the same drag event
+ TestIWindow iwindow = (TestIWindow) mWindow.mClient;
+ final ArrayList<DragEvent> dragEvents = new ArrayList<>();
+ iwindow.setDragEventJournal(dragEvents);
+
+ startDrag(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ,
+ ClipData.newPlainText("label", "text"), () -> {
+ // Verify the start-drag event is sent for invisible windows
+ final DragEvent dragEvent = dragEvents.get(0);
+ assertTrue(dragEvent.getAction() == ACTION_DRAG_STARTED);
+
+ // Verify after consuming that the drag surface is relinquished
+ try {
+ mTarget.mDeferDragStateClosed = true;
+
+ // Verify the drop event includes the drag surface
+ mTarget.handleMotionEvent(false, 0, 0);
+ final DragEvent dropEvent = dragEvents.get(dragEvents.size() - 1);
+ assertTrue(dropEvent.getDragSurface() != null);
+
+ mTarget.reportDropResult(iwindow, true);
+ } finally {
+ mTarget.mDeferDragStateClosed = false;
+ }
+ assertTrue(mTarget.dragSurfaceRelinquished());
+ });
+ }
+
+ @Test
+ public void testValidateAppActivityArguments() {
+ final Session session = new Session(mWm, new IWindowSessionCallback.Stub() {
+ @Override
+ public void onAnimatorScaleChanged(float scale) {}
+ });
+ try {
+ session.validateAndResolveDragMimeTypeExtras(
+ createClipDataForActivity(null, null), 0);
+ fail("Expected failure without pending intent and user");
+ } catch (IllegalArgumentException e) {
+ // Expected failure
+ }
+ try {
+ session.validateAndResolveDragMimeTypeExtras(
+ createClipDataForActivity(mock(PendingIntent.class), null), 0);
+ fail("Expected failure without user");
+ } catch (IllegalArgumentException e) {
+ // Expected failure
+ }
+ try {
+ session.validateAndResolveDragMimeTypeExtras(
+ createClipDataForActivity(null, mock(UserHandle.class)), 0);
+ fail("Expected failure without pending intent");
+ } catch (IllegalArgumentException e) {
+ // Expected failure
+ }
+ }
+
+ private ClipData createClipDataForActivity(PendingIntent pi, UserHandle user) {
+ final Intent data = new Intent();
+ if (pi != null) {
+ data.putExtra(ClipDescription.EXTRA_PENDING_INTENT, (Parcelable) pi);
+ }
+ if (user != null) {
+ data.putExtra(Intent.EXTRA_USER, user);
+ }
+ final ClipData clipData = new ClipData(
+ new ClipDescription("drag", new String[] {
+ MIMETYPE_APPLICATION_ACTIVITY}),
+ new ClipData.Item(data));
+ return clipData;
+ }
+
+ @Test
+ public void testValidateAppShortcutArguments() {
+ final Session session = new Session(mWm, new IWindowSessionCallback.Stub() {
+ @Override
+ public void onAnimatorScaleChanged(float scale) {}
+ });
+ try {
+ final ClipData clipData = new ClipData(
+ new ClipDescription("drag", new String[] { MIMETYPE_APPLICATION_SHORTCUT }),
+ new ClipData.Item(new Intent()));
+
+ session.validateAndResolveDragMimeTypeExtras(clipData, TEST_UID);
+ fail("Expected failure without shortcut id");
+ } catch (IllegalArgumentException e) {
+ // Expected failure
+ }
+ }
+
+ @Test
+ public void testValidateAppTaskArguments() {
+ final Session session = new Session(mWm, new IWindowSessionCallback.Stub() {
+ @Override
+ public void onAnimatorScaleChanged(float scale) {}
+ });
+ try {
+ final ClipData clipData = new ClipData(
+ new ClipDescription("drag", new String[] { MIMETYPE_APPLICATION_TASK }),
+ new ClipData.Item(new Intent()));
+
+ session.validateAndResolveDragMimeTypeExtras(clipData, TEST_UID);
+ fail("Expected failure without task id");
+ } catch (IllegalArgumentException e) {
+ // Expected failure
+ }
+ }
+
+ private void doDragAndDrop(int flags, ClipData data, float dropX, float dropY) {
+ startDrag(flags, data, () -> {
+ mTarget.handleMotionEvent(false, dropX, dropY);
+ mToken = mWindow.mClient.asBinder();
+ });
+ }
+
+ private void startDrag(int flag, ClipData data, Runnable r) {
final SurfaceSession appSession = new SurfaceSession();
try {
final SurfaceControl surface = new SurfaceControl.Builder(appSession)
@@ -174,13 +336,10 @@
assertTrue(mWm.mInputManager.transferTouchFocus(new InputChannel(),
new InputChannel()));
- mToken = mTarget.performDrag(
- new SurfaceSession(), 0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0,
- data);
+ mToken = mTarget.performDrag(0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0, data);
assertNotNull(mToken);
- mTarget.handleMotionEvent(false, dropX, dropY);
- mToken = mWindow.mClient.asBinder();
+ r.run();
} finally {
appSession.kill();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 7fb7d40..2985796 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -476,6 +476,26 @@
assertFalse(wallpaperWindowToken.hasFixedRotationTransform());
}
+ @Test
+ public void testIsAnimatingByRecents() {
+ final ActivityRecord homeActivity = createHomeActivity();
+ final Task rootTask = createTaskStackOnDisplay(mDefaultDisplay);
+ final Task childTask = createTaskInStack(rootTask, 0 /* userId */);
+ final Task leafTask = createTaskInStack(childTask, 0 /* userId */);
+ spyOn(leafTask);
+ doReturn(true).when(leafTask).isVisible();
+
+ initializeRecentsAnimationController(mController, homeActivity);
+
+ // Verify RecentsAnimationController will animate visible leaf task by default.
+ verify(mController).addAnimation(eq(leafTask), anyBoolean(), anyBoolean(), eq(null));
+ assertTrue(leafTask.isAnimatingByRecents());
+
+ // Make sure isAnimatingByRecents will also return true when it called by the parent task.
+ assertTrue(rootTask.isAnimatingByRecents());
+ assertTrue(childTask.isAnimatingByRecents());
+ }
+
private ActivityRecord createHomeActivity() {
final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService)
.setStack(mRootHomeTask)
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index f154073..d68dde5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -28,6 +28,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.Task.ActivityState.STOPPED;
@@ -35,8 +36,11 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
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.anyString;
+import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.doCallRealMethod;
import android.app.ActivityManager;
@@ -539,7 +543,7 @@
addStatusBar(mActivity.mDisplayContent);
mActivity.setVisible(false);
- mActivity.mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_OPEN,
+ mActivity.mDisplayContent.prepareAppTransitionOld(WindowManager.TRANSIT_OLD_ACTIVITY_OPEN,
false /* alwaysKeepCurrent */);
mActivity.mDisplayContent.mOpeningApps.add(mActivity);
final float maxAspect = 1.8f;
@@ -584,6 +588,179 @@
assertTrue(statusBarController.isTransparentAllowed(w));
}
+ @Test
+ public void testDisplayIgnoreOrientationRequest_fixedOrientationAppLaunchedInTaskLetterbox() {
+ // Set up a display in landscape and ignoring orientation request.
+ setUpDisplaySizeWithApp(2800, 1400);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ // Portrait fixed app without max aspect.
+ prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT);
+
+ final Rect displayBounds = mActivity.mDisplayContent.getBounds();
+ final Rect taskBounds = mTask.getBounds();
+ final Rect activityBounds = mActivity.getBounds();
+
+ // Display shouldn't be rotated.
+ assertEquals(SCREEN_ORIENTATION_UNSPECIFIED,
+ mActivity.mDisplayContent.getLastOrientation());
+ assertTrue(displayBounds.width() > displayBounds.height());
+
+ // App should launch in task level letterboxing.
+ assertTrue(mTask.isTaskLetterboxed());
+ assertFalse(mActivity.inSizeCompatMode());
+ assertEquals(taskBounds, activityBounds);
+
+ // Task bounds should be 700x1400 with the ratio as the display.
+ assertEquals(displayBounds.height(), taskBounds.height());
+ assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+ taskBounds.width());
+ }
+
+ @Test
+ public void testDisplayIgnoreOrientationRequest_taskLetterboxBecameSizeCompatAfterRotate() {
+ // Set up a display in landscape and ignoring orientation request.
+ setUpDisplaySizeWithApp(2800, 1400);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ // Portrait fixed app without max aspect.
+ prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT);
+
+ final Rect activityBounds = mActivity.getBounds();
+
+ // Rotate display to portrait.
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+ final Rect displayBounds = mActivity.mDisplayContent.getBounds();
+ final Rect newActivityBounds = mActivity.getBounds();
+ assertTrue(displayBounds.width() < displayBounds.height());
+
+ // App should be in size compat.
+ assertFalse(mTask.isTaskLetterboxed());
+ assertScaled();
+ assertEquals(activityBounds.width(), newActivityBounds.width());
+ assertEquals(activityBounds.height(), newActivityBounds.height());
+ }
+
+ @Test
+ public void testDisplayIgnoreOrientationRequest_sizeCompatAfterRotate() {
+ // Set up a display in portrait and ignoring orientation request.
+ setUpDisplaySizeWithApp(1400, 2800);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ // Portrait fixed app without max aspect.
+ prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT);
+
+ Rect displayBounds = mActivity.mDisplayContent.getBounds();
+ Rect activityBounds = mActivity.getBounds();
+
+ // App should launch in fullscreen.
+ assertFalse(mTask.isTaskLetterboxed());
+ assertFalse(mActivity.inSizeCompatMode());
+ assertEquals(displayBounds, activityBounds);
+
+ // Rotate display to landscape.
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+ displayBounds = mActivity.mDisplayContent.getBounds();
+ activityBounds = mActivity.getBounds();
+ assertTrue(displayBounds.width() > displayBounds.height());
+
+ // App should be in size compat.
+ assertFalse(mTask.isTaskLetterboxed());
+ assertScaled();
+
+ // App bounds should be 700x1400 with the ratio as the display.
+ assertEquals(displayBounds.height(), activityBounds.height());
+ assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+ activityBounds.width());
+ }
+
+ @Test
+ public void testDisplayIgnoreOrientationRequest_newLaunchedOrientationAppInTaskLetterbox() {
+ // Set up a display in landscape and ignoring orientation request.
+ setUpDisplaySizeWithApp(2800, 1400);
+ final DisplayContent display = mActivity.mDisplayContent;
+ display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ // Portrait fixed app without max aspect.
+ prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT);
+
+ assertTrue(mTask.isTaskLetterboxed());
+ assertFalse(mActivity.inSizeCompatMode());
+
+ // Launch another portrait fixed app.
+ spyOn(mTask);
+ setBooted(display.mWmService.mAtmService);
+ final ActivityRecord newActivity = new ActivityBuilder(display.mWmService.mAtmService)
+ .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+ .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+ .setTask(mTask)
+ .build();
+
+ // Update with new activity requested orientation and recompute bounds with no previous
+ // size compat cache.
+ verify(mTask).onDescendantOrientationChanged(any(), same(newActivity));
+ verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt());
+ verify(newActivity).clearSizeCompatMode(false /* recomputeTask */);
+
+ final Rect displayBounds = display.getBounds();
+ final Rect taskBounds = mTask.getBounds();
+ final Rect newActivityBounds = newActivity.getBounds();
+
+ // Task and app bounds should be 700x1400 with the ratio as the display.
+ assertTrue(mTask.isTaskLetterboxed());
+ assertFalse(newActivity.inSizeCompatMode());
+ assertEquals(taskBounds, newActivityBounds);
+ assertEquals(displayBounds.height(), taskBounds.height());
+ assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+ taskBounds.width());
+ }
+
+ @Test
+ public void testDisplayIgnoreOrientationRequest_newLaunchedMaxAspectApp() {
+ // Set up a display in landscape and ignoring orientation request.
+ setUpDisplaySizeWithApp(2800, 1400);
+ final DisplayContent display = mActivity.mDisplayContent;
+ display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ // Portrait fixed app without max aspect.
+ prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT);
+
+ assertTrue(mTask.isTaskLetterboxed());
+ assertFalse(mActivity.inSizeCompatMode());
+
+ // Launch another portrait fixed app with max aspect ratio as 1.3.
+ spyOn(mTask);
+ setBooted(display.mWmService.mAtmService);
+ final ActivityRecord newActivity = new ActivityBuilder(display.mWmService.mAtmService)
+ .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+ .setMaxAspectRatio(1.3f)
+ .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+ .setTask(mTask)
+ .build();
+
+ // Update with new activity requested orientation and recompute bounds with no previous
+ // size compat cache.
+ verify(mTask).onDescendantOrientationChanged(any(), same(newActivity));
+ verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt());
+ verify(newActivity).clearSizeCompatMode(false /* recomputeTask */);
+
+ final Rect displayBounds = display.getBounds();
+ final Rect taskBounds = mTask.getBounds();
+ final Rect newActivityBounds = newActivity.getBounds();
+
+ // Task bounds should be (1400 / 1.3 = 1076)x1400 with the app requested ratio.
+ assertTrue(mTask.isTaskLetterboxed());
+ assertEquals(displayBounds.height(), taskBounds.height());
+ assertEquals((long) Math.rint(taskBounds.height() / newActivity.info.maxAspectRatio),
+ taskBounds.width());
+
+ // App bounds should be fullscreen in Task bounds.
+ assertFalse(newActivity.inSizeCompatMode());
+ assertEquals(taskBounds, newActivityBounds);
+ }
+
private static WindowState addWindowToActivity(ActivityRecord activity) {
final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 7975899..2fa7589 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -420,8 +420,9 @@
// Without limiting to be inside the parent bounds, the out screen size should keep relative
// to the input bounds.
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
final ActivityRecord.CompatDisplayInsets compatIntsets =
- new ActivityRecord.CompatDisplayInsets(display, task);
+ new ActivityRecord.CompatDisplayInsets(display, activity);
task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatIntsets);
assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index ace0400..3203ccb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -23,6 +23,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -181,17 +182,27 @@
}
@Test
- public void testSwitchUser() {
+ public void testEnsureActivitiesVisible() {
final Task rootTask = createTaskStackOnDisplay(mDisplayContent);
- final Task childTask = createTaskInStack(rootTask, 0 /* userId */);
- final Task leafTask1 = createTaskInStack(childTask, 10 /* userId */);
- final Task leafTask2 = createTaskInStack(childTask, 0 /* userId */);
- assertEquals(1, rootTask.getChildCount());
- assertEquals(leafTask2, childTask.getTopChild());
+ final Task leafTask1 = createTaskInStack(rootTask, 0 /* userId */);
+ final Task leafTask2 = createTaskInStack(rootTask, 0 /* userId */);
+ final ActivityRecord activity1 = createActivityRecordInTask(mDisplayContent, leafTask1);
+ final ActivityRecord activity2 = createActivityRecordInTask(mDisplayContent, leafTask2);
- doReturn(true).when(leafTask1).showToCurrentUser();
- rootTask.switchUser(10);
- assertEquals(1, rootTask.getChildCount());
- assertEquals(leafTask1, childTask.getTopChild());
+ // Check visibility of occluded tasks
+ doReturn(false).when(leafTask1).shouldBeVisible(any());
+ doReturn(true).when(leafTask2).shouldBeVisible(any());
+ rootTask.ensureActivitiesVisible(
+ null /* starting */ , 0 /* configChanges */, false /* preserveWindows */);
+ assertFalse(activity1.isVisible());
+ assertTrue(activity2.isVisible());
+
+ // Check visibility of not occluded tasks
+ doReturn(true).when(leafTask1).shouldBeVisible(any());
+ doReturn(true).when(leafTask2).shouldBeVisible(any());
+ rootTask.ensureActivitiesVisible(
+ null /* starting */ , 0 /* configChanges */, false /* preserveWindows */);
+ assertTrue(activity1.isVisible());
+ assertTrue(activity2.isVisible());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index ee16a76..e95efe7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -150,7 +150,7 @@
newDisplay.onRequestedOverrideConfigurationChanged(c);
if (!mCanRotate) {
final DisplayRotation displayRotation = newDisplay.getDisplayRotation();
- doReturn(false).when(displayRotation).respectAppRequestedOrientation();
+ doReturn(true).when(displayRotation).isFixedToUserRotation();
}
// Please add stubbing before this line. Services will start using this display in other
// threads immediately after adding it to hierarchy. Calling doAnswer() type of stubbing
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index db1c12f..b78d6bb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -30,7 +30,12 @@
import com.android.internal.os.IResultReceiver;
+import java.util.ArrayList;
+
public class TestIWindow extends IWindow.Stub {
+
+ private ArrayList<DragEvent> mDragEvents;
+
@Override
public void executeCommand(String command, String parameters,
ParcelFileDescriptor descriptor) throws RemoteException {
@@ -85,8 +90,16 @@
public void dispatchWallpaperCommand(String action, int x, int y, int z, Bundle extras,
boolean sync) throws RemoteException {
}
+
+ public void setDragEventJournal(ArrayList<DragEvent> journal) {
+ mDragEvents = journal;
+ }
+
@Override
public void dispatchDragEvent(DragEvent event) throws RemoteException {
+ if (mDragEvents != null) {
+ mDragEvents.add(DragEvent.obtain(event));
+ }
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index ce22205..feb509c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -18,7 +18,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -59,7 +59,7 @@
opening.mVisibleRequested = true;
ArrayMap<WindowContainer, Transition.ChangeInfo> participants = new ArrayMap<>();
- int transitType = TRANSIT_TASK_OPEN;
+ int transitType = TRANSIT_OLD_TASK_OPEN;
// Check basic both tasks participating
participants.put(oldTask, new Transition.ChangeInfo());
@@ -113,7 +113,7 @@
opening2.mVisibleRequested = true;
ArrayMap<WindowContainer, Transition.ChangeInfo> participants = new ArrayMap<>();
- int transitType = TRANSIT_TASK_OPEN;
+ int transitType = TRANSIT_OLD_TASK_OPEN;
// Check full promotion from leaf
participants.put(oldTask, new Transition.ChangeInfo());
@@ -152,7 +152,7 @@
showing2.mVisibleRequested = true;
ArrayMap<WindowContainer, Transition.ChangeInfo> participants = new ArrayMap<>();
- int transitType = TRANSIT_TASK_OPEN;
+ int transitType = TRANSIT_OLD_TASK_OPEN;
// Check promotion to DisplayArea
participants.put(showing, new Transition.ChangeInfo());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 36f3a21..f5d6889 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -22,7 +22,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -919,7 +919,7 @@
}
}, 0, 0, false);
adapter.setCallingPidUid(123, 456);
- wc.getDisplayContent().prepareAppTransition(TRANSIT_TASK_OPEN, false);
+ wc.getDisplayContent().prepareAppTransitionOld(TRANSIT_OLD_TASK_OPEN, false);
wc.getDisplayContent().mAppTransition.overridePendingAppTransitionRemote(adapter);
spyOn(wc);
doReturn(true).when(wc).okToAnimate();
@@ -930,7 +930,7 @@
// of the animation.
ArrayList<WindowContainer<WindowState>> sources = new ArrayList<>();
sources.add(act);
- assertTrue(wc.applyAnimation(null, TRANSIT_TASK_OPEN, true, false, sources));
+ assertTrue(wc.applyAnimation(null, TRANSIT_OLD_TASK_OPEN, true, false, sources));
assertEquals(act, wc.getTopMostActivity());
assertTrue(wc.isAnimating());
@@ -943,7 +943,7 @@
// Make sure animation finish callback will be received and reset animating state after
// animation finish.
- wc.getDisplayContent().mAppTransition.goodToGo(TRANSIT_TASK_OPEN, act,
+ wc.getDisplayContent().mAppTransition.goodToGo(TRANSIT_OLD_TASK_OPEN, act,
mDisplayContent.mOpeningApps);
verify(wc).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION), any());
assertFalse(wc.isAnimating());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index c790f84..b4480ae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -479,38 +479,22 @@
@Test
public void testCreateDeleteRootTasks() {
- ITaskOrganizer listener = new ITaskOrganizer.Stub() {
- @Override
- public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { }
+ DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
- @Override
- public void onTaskVanished(RunningTaskInfo container) { }
-
- @Override
- public void onTaskInfoChanged(RunningTaskInfo info) {
- }
-
- @Override
- public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
- }
- };
- mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
-
- RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- Display.DEFAULT_DISPLAY,
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).getTaskInfo();
+ Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+ dc, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null);
+ RunningTaskInfo info1 = task1.getTaskInfo();
assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
info1.configuration.windowConfiguration.getWindowingMode());
assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
- RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- Display.DEFAULT_DISPLAY,
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).getTaskInfo();
+ Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+ dc, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
+ RunningTaskInfo info2 = task2.getTaskInfo();
assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
info2.configuration.windowConfiguration.getWindowingMode());
assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType);
- DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
List<Task> infos = getTasksCreatedByOrganizer(dc);
assertEquals(2, infos.size());
@@ -538,8 +522,9 @@
}
};
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
- RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).getTaskInfo();
+ Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+ mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
+ RunningTaskInfo info1 = task.getTaskInfo();
final Task stack = createTaskStackOnDisplay(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
@@ -596,8 +581,9 @@
}
};
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
- RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).getTaskInfo();
+ Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+ mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
+ RunningTaskInfo info1 = task.getTaskInfo();
lastReportedTiles.clear();
called[0] = false;
@@ -657,10 +643,13 @@
}
};
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
- RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).getTaskInfo();
- RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).getTaskInfo();
+
+ Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+ mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null);
+ RunningTaskInfo info1 = task1.getTaskInfo();
+ Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+ mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
+ RunningTaskInfo info2 = task2.getTaskInfo();
final int initialRootTaskCount = mWm.mAtmService.mTaskOrganizerController.getRootTasks(
mDisplayContent.mDisplayId, null /* activityTypes */).size();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 924b286..62f04a1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -316,6 +316,15 @@
return createWindow(null, type, activity, name);
}
+ // TODO: Move these calls to a builder?
+ WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name,
+ IWindow iwindow) {
+ final WindowToken token = createWindowToken(
+ dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
+ return createWindow(parent, type, token, name, 0 /* ownerId */,
+ false /* ownerCanAddInternalSystemWindow */, iwindow);
+ }
+
WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) {
final WindowToken token = createWindowToken(
dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
@@ -350,8 +359,14 @@
WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
int ownerId, boolean ownerCanAddInternalSystemWindow) {
+ return createWindow(parent, type, token, name, ownerId, ownerCanAddInternalSystemWindow,
+ mIWindow);
+ }
+
+ WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
+ int ownerId, boolean ownerCanAddInternalSystemWindow, IWindow iwindow) {
return createWindow(parent, type, token, name, ownerId, UserHandle.getUserId(ownerId),
- ownerCanAddInternalSystemWindow, mWm, mMockSession, mIWindow,
+ ownerCanAddInternalSystemWindow, mWm, mMockSession, iwindow,
mSystemServicesTestRule.getPowerManagerWrapper());
}
@@ -958,8 +973,9 @@
final int taskId = mTaskId >= 0 ? mTaskId : mTaskDisplayArea.getNextStackId();
if (mParentTask == null) {
task = mTaskDisplayArea.createStackUnchecked(
- mWindowingMode, mActivityType, taskId, mOnTop, mActivityInfo,
- mIntent, false /* createdByOrganizer */);
+ mWindowingMode, mActivityType, taskId, mOnTop, mActivityInfo, mIntent,
+ false /* createdByOrganizer */, false /* deferTaskAppear */,
+ null /* launchCookie */);
} else {
task = new Task(mSupervisor.mService, taskId, mActivityInfo,
mIntent /*intent*/, mVoiceSession, null /*_voiceInteractor*/,
@@ -1001,20 +1017,17 @@
// moves everything to secondary. Most tests expect this since sysui usually does it.
boolean mMoveToSecondaryOnEnter = true;
int mDisplayId;
- TestSplitOrganizer(ActivityTaskManagerService service, int displayId) {
+ TestSplitOrganizer(ActivityTaskManagerService service, DisplayContent display) {
mService = service;
- mDisplayId = displayId;
+ mDisplayId = display.mDisplayId;
mService.mTaskOrganizerController.registerTaskOrganizer(this);
- WindowContainerToken primary = mService.mTaskOrganizerController.createRootTask(
- displayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).getTaskInfo().token;
- mPrimary = WindowContainer.fromBinder(primary.asBinder()).asTask();
- WindowContainerToken secondary = mService.mTaskOrganizerController.createRootTask(
- displayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).getTaskInfo().token;
- mSecondary = WindowContainer.fromBinder(secondary.asBinder()).asTask();
+ mPrimary = mService.mTaskOrganizerController.createRootTask(
+ display, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null);
+ mSecondary = mService.mTaskOrganizerController.createRootTask(
+ display, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);;
}
TestSplitOrganizer(ActivityTaskManagerService service) {
- this(service,
- service.mStackSupervisor.mRootWindowContainer.getDefaultDisplay().mDisplayId);
+ this(service, service.mStackSupervisor.mRootWindowContainer.getDefaultDisplay());
}
public void setMoveToSecondaryOnEnter(boolean move) {
mMoveToSecondaryOnEnter = move;
diff --git a/services/usage/Android.bp b/services/usage/Android.bp
index 463673f..80f040b 100644
--- a/services/usage/Android.bp
+++ b/services/usage/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.usage",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [":services.usage-sources"],
libs: ["services.core"],
}
diff --git a/services/usb/Android.bp b/services/usb/Android.bp
index 4e98409..1a23c8c 100644
--- a/services/usb/Android.bp
+++ b/services/usb/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.usb",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [":services.usb-sources"],
libs: [
diff --git a/services/voiceinteraction/Android.bp b/services/voiceinteraction/Android.bp
index 47129ad..02061be 100644
--- a/services/voiceinteraction/Android.bp
+++ b/services/voiceinteraction/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.voiceinteraction",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [":services.voiceinteraction-sources"],
libs: ["services.core"],
}
diff --git a/services/wifi/Android.bp b/services/wifi/Android.bp
index 3975fd2..fcfcbeb 100644
--- a/services/wifi/Android.bp
+++ b/services/wifi/Android.bp
@@ -7,7 +7,7 @@
java_library_static {
name: "services.wifi",
- defaults: ["services_defaults"],
+ defaults: ["platform_service_defaults"],
srcs: [
":services.wifi-sources",
],
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 82da447..ae485d5 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -32,6 +32,7 @@
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -1605,6 +1606,30 @@
}
/**
+ * Returns whether the caller has {@link InCallService} access for companion apps.
+ *
+ * A companion app is an app associated with a physical wearable device via the
+ * {@link android.companion.CompanionDeviceManager} API.
+ *
+ * @return {@code true} if the caller has {@link InCallService} access for
+ * companion app; {@code false} otherwise.
+ */
+ public boolean hasCompanionInCallServiceAccess() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().hasCompanionInCallServiceAccess(
+ mContext.getOpPackageName());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling hasCompanionInCallServiceAccess().", e);
+ if (!isSystemProcess()) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+ return false;
+ }
+
+ /**
* Returns whether there is an ongoing call originating from a managed
* {@link ConnectionService}. An ongoing call can be in dialing, ringing, active or holding
* states.
@@ -2416,6 +2441,10 @@
}
}
+ private boolean isSystemProcess() {
+ return Process.myUid() == Process.SYSTEM_UID;
+ }
+
private ITelecomService getTelecomService() {
if (mTelecomServiceOverride != null) {
return mTelecomServiceOverride;
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 7c6f1df..e636b93 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -179,6 +179,11 @@
boolean isInCall(String callingPackage, String callingFeatureId);
/**
+ * @see TelecomServiceImpl#hasCompanionInCallServiceAccess
+ */
+ boolean hasCompanionInCallServiceAccess(String callingPackage);
+
+ /**
* @see TelecomServiceImpl#isInManagedCall
*/
boolean isInManagedCall(String callingPackage, String callingFeatureId);
diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt
deleted file mode 100644
index 5eaa6c8..0000000
--- a/telephony/api/system-current.txt
+++ /dev/null
@@ -1,2088 +0,0 @@
-// Signature format: 2.0
-package android.telephony {
-
- public final class AccessNetworkConstants {
- field public static final int TRANSPORT_TYPE_INVALID = -1; // 0xffffffff
- }
-
- public static final class AccessNetworkConstants.NgranBands {
- method public static int getFrequencyRangeGroup(int);
- field public static final int FREQUENCY_RANGE_GROUP_1 = 1; // 0x1
- field public static final int FREQUENCY_RANGE_GROUP_2 = 2; // 0x2
- field public static final int FREQUENCY_RANGE_GROUP_UNKNOWN = 0; // 0x0
- }
-
- public final class BarringInfo implements android.os.Parcelable {
- ctor public BarringInfo();
- method @NonNull public android.telephony.BarringInfo createLocationInfoSanitizedCopy();
- }
-
- public final class CallAttributes implements android.os.Parcelable {
- ctor public CallAttributes(@NonNull android.telephony.PreciseCallState, int, @NonNull android.telephony.CallQuality);
- method public int describeContents();
- method @NonNull public android.telephony.CallQuality getCallQuality();
- method public int getNetworkType();
- method @NonNull public android.telephony.PreciseCallState getPreciseCallState();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallAttributes> CREATOR;
- }
-
- public final class CallForwardingInfo implements android.os.Parcelable {
- ctor public CallForwardingInfo(boolean, int, @Nullable String, int);
- method public int describeContents();
- method @Nullable public String getNumber();
- method public int getReason();
- method public int getTimeoutSeconds();
- method public boolean isEnabled();
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallForwardingInfo> CREATOR;
- field public static final int REASON_ALL = 4; // 0x4
- field public static final int REASON_ALL_CONDITIONAL = 5; // 0x5
- field public static final int REASON_BUSY = 1; // 0x1
- field public static final int REASON_NOT_REACHABLE = 3; // 0x3
- field public static final int REASON_NO_REPLY = 2; // 0x2
- field public static final int REASON_UNCONDITIONAL = 0; // 0x0
- }
-
- public final class CallQuality implements android.os.Parcelable {
- ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int);
- ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int, boolean, boolean, boolean);
- method public int describeContents();
- method public int getAverageRelativeJitter();
- method public int getAverageRoundTripTime();
- method public int getCallDuration();
- method public int getCodecType();
- method public int getDownlinkCallQualityLevel();
- method public int getMaxRelativeJitter();
- method public int getNumRtpPacketsNotReceived();
- method public int getNumRtpPacketsReceived();
- method public int getNumRtpPacketsTransmitted();
- method public int getNumRtpPacketsTransmittedLost();
- method public int getUplinkCallQualityLevel();
- method public boolean isIncomingSilenceDetectedAtCallSetup();
- method public boolean isOutgoingSilenceDetectedAtCallSetup();
- method public boolean isRtpInactivityDetected();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int CALL_QUALITY_BAD = 4; // 0x4
- field public static final int CALL_QUALITY_EXCELLENT = 0; // 0x0
- field public static final int CALL_QUALITY_FAIR = 2; // 0x2
- field public static final int CALL_QUALITY_GOOD = 1; // 0x1
- field public static final int CALL_QUALITY_NOT_AVAILABLE = 5; // 0x5
- field public static final int CALL_QUALITY_POOR = 3; // 0x3
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallQuality> CREATOR;
- }
-
- public class CarrierConfigManager {
- method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultCarrierServicePackageName();
- method @NonNull public static android.os.PersistableBundle getDefaultConfig();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void overrideConfig(int, @Nullable android.os.PersistableBundle);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void updateConfigForPhoneId(int, String);
- field public static final String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
- field public static final String KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL = "support_cdma_1x_voice_calls_bool";
- }
-
- public static final class CarrierConfigManager.Wifi {
- field public static final String KEY_HOTSPOT_MAX_CLIENT_COUNT = "wifi.hotspot_maximum_client_count";
- field public static final String KEY_PREFIX = "wifi.";
- }
-
- public final class CarrierRestrictionRules implements android.os.Parcelable {
- method @NonNull public java.util.List<java.lang.Boolean> areCarrierIdentifiersAllowed(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
- method public int describeContents();
- method @NonNull public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers();
- method public int getDefaultCarrierRestriction();
- method @NonNull public java.util.List<android.service.carrier.CarrierIdentifier> getExcludedCarriers();
- method public int getMultiSimPolicy();
- method public boolean isAllCarriersAllowed();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int CARRIER_RESTRICTION_DEFAULT_ALLOWED = 1; // 0x1
- field public static final int CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED = 0; // 0x0
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CarrierRestrictionRules> CREATOR;
- field public static final int MULTISIM_POLICY_NONE = 0; // 0x0
- field public static final int MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT = 1; // 0x1
- }
-
- public static final class CarrierRestrictionRules.Builder {
- ctor public CarrierRestrictionRules.Builder();
- method @NonNull public android.telephony.CarrierRestrictionRules build();
- method @NonNull public android.telephony.CarrierRestrictionRules.Builder setAllCarriersAllowed();
- method @NonNull public android.telephony.CarrierRestrictionRules.Builder setAllowedCarriers(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
- method @NonNull public android.telephony.CarrierRestrictionRules.Builder setDefaultCarrierRestriction(int);
- method @NonNull public android.telephony.CarrierRestrictionRules.Builder setExcludedCarriers(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
- method @NonNull public android.telephony.CarrierRestrictionRules.Builder setMultiSimPolicy(int);
- }
-
- public class CbGeoUtils {
- }
-
- public static class CbGeoUtils.Circle implements android.telephony.CbGeoUtils.Geometry {
- ctor public CbGeoUtils.Circle(@NonNull android.telephony.CbGeoUtils.LatLng, double);
- method public boolean contains(@NonNull android.telephony.CbGeoUtils.LatLng);
- method @NonNull public android.telephony.CbGeoUtils.LatLng getCenter();
- method public double getRadius();
- }
-
- public static interface CbGeoUtils.Geometry {
- method public boolean contains(@NonNull android.telephony.CbGeoUtils.LatLng);
- }
-
- public static class CbGeoUtils.LatLng {
- ctor public CbGeoUtils.LatLng(double, double);
- method public double distance(@NonNull android.telephony.CbGeoUtils.LatLng);
- method @NonNull public android.telephony.CbGeoUtils.LatLng subtract(@NonNull android.telephony.CbGeoUtils.LatLng);
- field public final double lat;
- field public final double lng;
- }
-
- public static class CbGeoUtils.Polygon implements android.telephony.CbGeoUtils.Geometry {
- ctor public CbGeoUtils.Polygon(@NonNull java.util.List<android.telephony.CbGeoUtils.LatLng>);
- method public boolean contains(@NonNull android.telephony.CbGeoUtils.LatLng);
- method @NonNull public java.util.List<android.telephony.CbGeoUtils.LatLng> getVertices();
- }
-
- public abstract class CellBroadcastService extends android.app.Service {
- ctor public CellBroadcastService();
- method @NonNull @WorkerThread public abstract CharSequence getCellBroadcastAreaInfo(int);
- method public android.os.IBinder onBind(@Nullable android.content.Intent);
- method public abstract void onCdmaCellBroadcastSms(int, @NonNull byte[], int);
- method public abstract void onCdmaScpMessage(int, @NonNull java.util.List<android.telephony.cdma.CdmaSmsCbProgramData>, @NonNull String, @NonNull java.util.function.Consumer<android.os.Bundle>);
- method public abstract void onGsmCellBroadcastSms(int, @NonNull byte[]);
- field public static final String CELL_BROADCAST_SERVICE_INTERFACE = "android.telephony.CellBroadcastService";
- }
-
- public abstract class CellIdentity implements android.os.Parcelable {
- method @NonNull public abstract android.telephony.CellLocation asCellLocation();
- method @NonNull public abstract android.telephony.CellIdentity sanitizeLocationInfo();
- }
-
- public final class CellIdentityCdma extends android.telephony.CellIdentity {
- method @NonNull public android.telephony.cdma.CdmaCellLocation asCellLocation();
- method @NonNull public android.telephony.CellIdentityCdma sanitizeLocationInfo();
- }
-
- public final class CellIdentityGsm extends android.telephony.CellIdentity {
- method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation();
- method @NonNull public android.telephony.CellIdentityGsm sanitizeLocationInfo();
- }
-
- public final class CellIdentityLte extends android.telephony.CellIdentity {
- method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation();
- method @NonNull public android.telephony.CellIdentityLte sanitizeLocationInfo();
- }
-
- public final class CellIdentityNr extends android.telephony.CellIdentity {
- method @NonNull public android.telephony.CellLocation asCellLocation();
- method @NonNull public android.telephony.CellIdentityNr sanitizeLocationInfo();
- }
-
- public final class CellIdentityTdscdma extends android.telephony.CellIdentity {
- method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation();
- method @NonNull public android.telephony.CellIdentityTdscdma sanitizeLocationInfo();
- }
-
- public final class CellIdentityWcdma extends android.telephony.CellIdentity {
- method @NonNull public android.telephony.gsm.GsmCellLocation asCellLocation();
- method @NonNull public android.telephony.CellIdentityWcdma sanitizeLocationInfo();
- }
-
- public final class DataFailCause {
- field @Deprecated public static final int VSNCP_APN_UNATHORIZED = 2238; // 0x8be
- }
-
- public final class DataSpecificRegistrationInfo implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public android.telephony.LteVopsSupportInfo getLteVopsSupportInfo();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.DataSpecificRegistrationInfo> CREATOR;
- }
-
- public final class ImsiEncryptionInfo implements android.os.Parcelable {
- method public int describeContents();
- method @Nullable public String getKeyIdentifier();
- method @Nullable public java.security.PublicKey getPublicKey();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ImsiEncryptionInfo> CREATOR;
- }
-
- public final class LteVopsSupportInfo implements android.os.Parcelable {
- ctor public LteVopsSupportInfo(int, int);
- method public int describeContents();
- method public int getEmcBearerSupport();
- method public int getVopsSupport();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.LteVopsSupportInfo> CREATOR;
- field public static final int LTE_STATUS_NOT_AVAILABLE = 1; // 0x1
- field public static final int LTE_STATUS_NOT_SUPPORTED = 3; // 0x3
- field public static final int LTE_STATUS_SUPPORTED = 2; // 0x2
- }
-
- public class MbmsDownloadSession implements java.lang.AutoCloseable {
- field public static final String MBMS_DOWNLOAD_SERVICE_ACTION = "android.telephony.action.EmbmsDownload";
- }
-
- public class MbmsGroupCallSession implements java.lang.AutoCloseable {
- field public static final String MBMS_GROUP_CALL_SERVICE_ACTION = "android.telephony.action.EmbmsGroupCall";
- }
-
- public class MbmsStreamingSession implements java.lang.AutoCloseable {
- field public static final String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
- }
-
- public final class ModemActivityInfo implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public android.telephony.ModemActivityInfo getDelta(@NonNull android.telephony.ModemActivityInfo);
- method public long getIdleTimeMillis();
- method public static int getNumTxPowerLevels();
- method public long getReceiveTimeMillis();
- method public long getSleepTimeMillis();
- method public long getTimestampMillis();
- method public long getTransmitDurationMillisAtPowerLevel(int);
- method @NonNull public android.util.Range<java.lang.Integer> getTransmitPowerRange(int);
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR;
- field public static final int TX_POWER_LEVEL_0 = 0; // 0x0
- field public static final int TX_POWER_LEVEL_1 = 1; // 0x1
- field public static final int TX_POWER_LEVEL_2 = 2; // 0x2
- field public static final int TX_POWER_LEVEL_3 = 3; // 0x3
- field public static final int TX_POWER_LEVEL_4 = 4; // 0x4
- }
-
- public final class NetworkRegistrationInfo implements android.os.Parcelable {
- method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo();
- method public int getRegistrationState();
- method public int getRejectCause();
- method public int getRoamingType();
- method public boolean isEmergencyEnabled();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int REGISTRATION_STATE_DENIED = 3; // 0x3
- field public static final int REGISTRATION_STATE_HOME = 1; // 0x1
- field public static final int REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING = 0; // 0x0
- field public static final int REGISTRATION_STATE_NOT_REGISTERED_SEARCHING = 2; // 0x2
- field public static final int REGISTRATION_STATE_ROAMING = 5; // 0x5
- field public static final int REGISTRATION_STATE_UNKNOWN = 4; // 0x4
- }
-
- public static final class NetworkRegistrationInfo.Builder {
- ctor public NetworkRegistrationInfo.Builder();
- method @NonNull public android.telephony.NetworkRegistrationInfo build();
- method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setAccessNetworkTechnology(int);
- method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setAvailableServices(@NonNull java.util.List<java.lang.Integer>);
- method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setCellIdentity(@Nullable android.telephony.CellIdentity);
- method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setDomain(int);
- method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setEmergencyOnly(boolean);
- method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRegisteredPlmn(@Nullable String);
- method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRegistrationState(int);
- method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRejectCause(int);
- method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setTransportType(int);
- }
-
- public abstract class NetworkService extends android.app.Service {
- ctor public NetworkService();
- method public android.os.IBinder onBind(android.content.Intent);
- method @Nullable public abstract android.telephony.NetworkService.NetworkServiceProvider onCreateNetworkServiceProvider(int);
- field public static final String SERVICE_INTERFACE = "android.telephony.NetworkService";
- }
-
- public abstract class NetworkService.NetworkServiceProvider implements java.lang.AutoCloseable {
- ctor public NetworkService.NetworkServiceProvider(int);
- method public abstract void close();
- method public final int getSlotIndex();
- method public final void notifyNetworkRegistrationInfoChanged();
- method public void requestNetworkRegistrationInfo(int, @NonNull android.telephony.NetworkServiceCallback);
- }
-
- public class NetworkServiceCallback {
- method public void onRequestNetworkRegistrationInfoComplete(int, @Nullable android.telephony.NetworkRegistrationInfo);
- field public static final int RESULT_ERROR_BUSY = 3; // 0x3
- field public static final int RESULT_ERROR_FAILED = 5; // 0x5
- field public static final int RESULT_ERROR_ILLEGAL_STATE = 4; // 0x4
- field public static final int RESULT_ERROR_INVALID_ARG = 2; // 0x2
- field public static final int RESULT_ERROR_UNSUPPORTED = 1; // 0x1
- field public static final int RESULT_SUCCESS = 0; // 0x0
- }
-
- public interface NumberVerificationCallback {
- method public default void onCallReceived(@NonNull String);
- method public default void onVerificationFailed(int);
- field public static final int REASON_CONCURRENT_REQUESTS = 4; // 0x4
- field public static final int REASON_IN_ECBM = 5; // 0x5
- field public static final int REASON_IN_EMERGENCY_CALL = 6; // 0x6
- field public static final int REASON_NETWORK_NOT_AVAILABLE = 2; // 0x2
- field public static final int REASON_TIMED_OUT = 1; // 0x1
- field public static final int REASON_TOO_MANY_CALLS = 3; // 0x3
- field public static final int REASON_UNSPECIFIED = 0; // 0x0
- }
-
- public final class PhoneNumberRange implements android.os.Parcelable {
- ctor public PhoneNumberRange(@NonNull String, @NonNull String, @NonNull String, @NonNull String);
- method public int describeContents();
- method public boolean matches(@NonNull String);
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhoneNumberRange> CREATOR;
- }
-
- public class PhoneNumberUtils {
- method @NonNull public static String getUsernameFromUriNumber(@NonNull String);
- method public static boolean isUriNumber(@Nullable String);
- method public static boolean isVoiceMailNumber(@NonNull android.content.Context, int, @Nullable String);
- }
-
- public final class PhysicalChannelConfig implements android.os.Parcelable {
- method public int describeContents();
- method public int getCellBandwidthDownlink();
- method public int getChannelNumber();
- method public int getConnectionStatus();
- method public int getNetworkType();
- method @IntRange(from=0, to=1007) public int getPhysicalCellId();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field public static final int CHANNEL_NUMBER_UNKNOWN = -1; // 0xffffffff
- field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1
- field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2
- field public static final int CONNECTION_UNKNOWN = -1; // 0xffffffff
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhysicalChannelConfig> CREATOR;
- field public static final int PHYSICAL_CELL_ID_UNKNOWN = -1; // 0xffffffff
- }
-
- public final class PreciseCallState implements android.os.Parcelable {
- ctor public PreciseCallState(int, int, int, int, int);
- method public int describeContents();
- method public int getBackgroundCallState();
- method public int getForegroundCallState();
- method public int getRingingCallState();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PreciseCallState> CREATOR;
- field public static final int PRECISE_CALL_STATE_ACTIVE = 1; // 0x1
- field public static final int PRECISE_CALL_STATE_ALERTING = 4; // 0x4
- field public static final int PRECISE_CALL_STATE_DIALING = 3; // 0x3
- field public static final int PRECISE_CALL_STATE_DISCONNECTED = 7; // 0x7
- field public static final int PRECISE_CALL_STATE_DISCONNECTING = 8; // 0x8
- field public static final int PRECISE_CALL_STATE_HOLDING = 2; // 0x2
- field public static final int PRECISE_CALL_STATE_IDLE = 0; // 0x0
- field public static final int PRECISE_CALL_STATE_INCOMING = 5; // 0x5
- field public static final int PRECISE_CALL_STATE_NOT_VALID = -1; // 0xffffffff
- field public static final int PRECISE_CALL_STATE_WAITING = 6; // 0x6
- }
-
- public final class PreciseDataConnectionState implements android.os.Parcelable {
- method @Deprecated @NonNull public String getDataConnectionApn();
- method @Deprecated public int getDataConnectionApnTypeBitMask();
- method @Deprecated public int getDataConnectionFailCause();
- method @Deprecated public int getDataConnectionState();
- method public int getId();
- }
-
- public final class PreciseDisconnectCause {
- field public static final int ACCESS_CLASS_BLOCKED = 260; // 0x104
- field public static final int ACCESS_INFORMATION_DISCARDED = 43; // 0x2b
- field public static final int ACM_LIMIT_EXCEEDED = 68; // 0x44
- field public static final int BEARER_CAPABILITY_NOT_AUTHORIZED = 57; // 0x39
- field public static final int BEARER_NOT_AVAIL = 58; // 0x3a
- field public static final int BEARER_SERVICE_NOT_IMPLEMENTED = 65; // 0x41
- field public static final int BUSY = 17; // 0x11
- field public static final int CALL_BARRED = 240; // 0xf0
- field public static final int CALL_REJECTED = 21; // 0x15
- field public static final int CDMA_ACCESS_BLOCKED = 1009; // 0x3f1
- field public static final int CDMA_ACCESS_FAILURE = 1006; // 0x3ee
- field public static final int CDMA_DROP = 1001; // 0x3e9
- field public static final int CDMA_INTERCEPT = 1002; // 0x3ea
- field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000; // 0x3e8
- field public static final int CDMA_NOT_EMERGENCY = 1008; // 0x3f0
- field public static final int CDMA_PREEMPTED = 1007; // 0x3ef
- field public static final int CDMA_REORDER = 1003; // 0x3eb
- field public static final int CDMA_RETRY_ORDER = 1005; // 0x3ed
- field public static final int CDMA_SO_REJECT = 1004; // 0x3ec
- field public static final int CHANNEL_NOT_AVAIL = 44; // 0x2c
- field public static final int CHANNEL_UNACCEPTABLE = 6; // 0x6
- field public static final int CONDITIONAL_IE_ERROR = 100; // 0x64
- field public static final int DESTINATION_OUT_OF_ORDER = 27; // 0x1b
- field public static final int ERROR_UNSPECIFIED = 65535; // 0xffff
- field public static final int FACILITY_REJECTED = 29; // 0x1d
- field public static final int FDN_BLOCKED = 241; // 0xf1
- field public static final int IMEI_NOT_ACCEPTED = 243; // 0xf3
- field public static final int IMSI_UNKNOWN_IN_VLR = 242; // 0xf2
- field public static final int INCOMING_CALLS_BARRED_WITHIN_CUG = 55; // 0x37
- field public static final int INCOMPATIBLE_DESTINATION = 88; // 0x58
- field public static final int INFORMATION_ELEMENT_NON_EXISTENT = 99; // 0x63
- field public static final int INTERWORKING_UNSPECIFIED = 127; // 0x7f
- field public static final int INVALID_MANDATORY_INFORMATION = 96; // 0x60
- field public static final int INVALID_NUMBER_FORMAT = 28; // 0x1c
- field public static final int INVALID_TRANSACTION_IDENTIFIER = 81; // 0x51
- field public static final int MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 101; // 0x65
- field public static final int MESSAGE_TYPE_NON_IMPLEMENTED = 97; // 0x61
- field public static final int MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 98; // 0x62
- field public static final int NETWORK_DETACH = 261; // 0x105
- field public static final int NETWORK_OUT_OF_ORDER = 38; // 0x26
- field public static final int NETWORK_REJECT = 252; // 0xfc
- field public static final int NETWORK_RESP_TIMEOUT = 251; // 0xfb
- field public static final int NORMAL = 16; // 0x10
- field public static final int NORMAL_UNSPECIFIED = 31; // 0x1f
- field public static final int NOT_VALID = -1; // 0xffffffff
- field public static final int NO_ANSWER_FROM_USER = 19; // 0x13
- field public static final int NO_CIRCUIT_AVAIL = 34; // 0x22
- field public static final int NO_DISCONNECT_CAUSE_AVAILABLE = 0; // 0x0
- field public static final int NO_ROUTE_TO_DESTINATION = 3; // 0x3
- field public static final int NO_USER_RESPONDING = 18; // 0x12
- field public static final int NO_VALID_SIM = 249; // 0xf9
- field public static final int NUMBER_CHANGED = 22; // 0x16
- field public static final int OEM_CAUSE_1 = 61441; // 0xf001
- field public static final int OEM_CAUSE_10 = 61450; // 0xf00a
- field public static final int OEM_CAUSE_11 = 61451; // 0xf00b
- field public static final int OEM_CAUSE_12 = 61452; // 0xf00c
- field public static final int OEM_CAUSE_13 = 61453; // 0xf00d
- field public static final int OEM_CAUSE_14 = 61454; // 0xf00e
- field public static final int OEM_CAUSE_15 = 61455; // 0xf00f
- field public static final int OEM_CAUSE_2 = 61442; // 0xf002
- field public static final int OEM_CAUSE_3 = 61443; // 0xf003
- field public static final int OEM_CAUSE_4 = 61444; // 0xf004
- field public static final int OEM_CAUSE_5 = 61445; // 0xf005
- field public static final int OEM_CAUSE_6 = 61446; // 0xf006
- field public static final int OEM_CAUSE_7 = 61447; // 0xf007
- field public static final int OEM_CAUSE_8 = 61448; // 0xf008
- field public static final int OEM_CAUSE_9 = 61449; // 0xf009
- field public static final int ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE = 70; // 0x46
- field public static final int OPERATOR_DETERMINED_BARRING = 8; // 0x8
- field public static final int OUT_OF_SRV = 248; // 0xf8
- field public static final int PREEMPTION = 25; // 0x19
- field public static final int PROTOCOL_ERROR_UNSPECIFIED = 111; // 0x6f
- field public static final int QOS_NOT_AVAIL = 49; // 0x31
- field public static final int RADIO_ACCESS_FAILURE = 253; // 0xfd
- field public static final int RADIO_INTERNAL_ERROR = 250; // 0xfa
- field public static final int RADIO_LINK_FAILURE = 254; // 0xfe
- field public static final int RADIO_LINK_LOST = 255; // 0xff
- field public static final int RADIO_OFF = 247; // 0xf7
- field public static final int RADIO_RELEASE_ABNORMAL = 259; // 0x103
- field public static final int RADIO_RELEASE_NORMAL = 258; // 0x102
- field public static final int RADIO_SETUP_FAILURE = 257; // 0x101
- field public static final int RADIO_UPLINK_FAILURE = 256; // 0x100
- field public static final int RECOVERY_ON_TIMER_EXPIRED = 102; // 0x66
- field public static final int REQUESTED_FACILITY_NOT_IMPLEMENTED = 69; // 0x45
- field public static final int REQUESTED_FACILITY_NOT_SUBSCRIBED = 50; // 0x32
- field public static final int RESOURCES_UNAVAILABLE_OR_UNSPECIFIED = 47; // 0x2f
- field public static final int SEMANTICALLY_INCORRECT_MESSAGE = 95; // 0x5f
- field public static final int SERVICE_OPTION_NOT_AVAILABLE = 63; // 0x3f
- field public static final int SERVICE_OR_OPTION_NOT_IMPLEMENTED = 79; // 0x4f
- field public static final int STATUS_ENQUIRY = 30; // 0x1e
- field public static final int SWITCHING_CONGESTION = 42; // 0x2a
- field public static final int TEMPORARY_FAILURE = 41; // 0x29
- field public static final int UNOBTAINABLE_NUMBER = 1; // 0x1
- field public static final int USER_NOT_MEMBER_OF_CUG = 87; // 0x57
- }
-
- public class ServiceState implements android.os.Parcelable {
- method @Nullable public android.telephony.NetworkRegistrationInfo getNetworkRegistrationInfo(int, int);
- method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForDomain(int);
- method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForTransportType(int);
- field public static final int ROAMING_TYPE_DOMESTIC = 2; // 0x2
- field public static final int ROAMING_TYPE_INTERNATIONAL = 3; // 0x3
- field public static final int ROAMING_TYPE_NOT_ROAMING = 0; // 0x0
- field public static final int ROAMING_TYPE_UNKNOWN = 1; // 0x1
- }
-
- public final class SmsCbCmasInfo implements android.os.Parcelable {
- ctor public SmsCbCmasInfo(int, int, int, int, int, int);
- method public int describeContents();
- method public int getCategory();
- method public int getCertainty();
- method public int getMessageClass();
- method public int getResponseType();
- method public int getSeverity();
- method public int getUrgency();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int CMAS_CATEGORY_CBRNE = 10; // 0xa
- field public static final int CMAS_CATEGORY_ENV = 7; // 0x7
- field public static final int CMAS_CATEGORY_FIRE = 5; // 0x5
- field public static final int CMAS_CATEGORY_GEO = 0; // 0x0
- field public static final int CMAS_CATEGORY_HEALTH = 6; // 0x6
- field public static final int CMAS_CATEGORY_INFRA = 9; // 0x9
- field public static final int CMAS_CATEGORY_MET = 1; // 0x1
- field public static final int CMAS_CATEGORY_OTHER = 11; // 0xb
- field public static final int CMAS_CATEGORY_RESCUE = 4; // 0x4
- field public static final int CMAS_CATEGORY_SAFETY = 2; // 0x2
- field public static final int CMAS_CATEGORY_SECURITY = 3; // 0x3
- field public static final int CMAS_CATEGORY_TRANSPORT = 8; // 0x8
- field public static final int CMAS_CATEGORY_UNKNOWN = -1; // 0xffffffff
- field public static final int CMAS_CERTAINTY_LIKELY = 1; // 0x1
- field public static final int CMAS_CERTAINTY_OBSERVED = 0; // 0x0
- field public static final int CMAS_CERTAINTY_UNKNOWN = -1; // 0xffffffff
- field public static final int CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY = 3; // 0x3
- field public static final int CMAS_CLASS_CMAS_EXERCISE = 5; // 0x5
- field public static final int CMAS_CLASS_EXTREME_THREAT = 1; // 0x1
- field public static final int CMAS_CLASS_OPERATOR_DEFINED_USE = 6; // 0x6
- field public static final int CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT = 0; // 0x0
- field public static final int CMAS_CLASS_REQUIRED_MONTHLY_TEST = 4; // 0x4
- field public static final int CMAS_CLASS_SEVERE_THREAT = 2; // 0x2
- field public static final int CMAS_CLASS_UNKNOWN = -1; // 0xffffffff
- field public static final int CMAS_RESPONSE_TYPE_ASSESS = 6; // 0x6
- field public static final int CMAS_RESPONSE_TYPE_AVOID = 5; // 0x5
- field public static final int CMAS_RESPONSE_TYPE_EVACUATE = 1; // 0x1
- field public static final int CMAS_RESPONSE_TYPE_EXECUTE = 3; // 0x3
- field public static final int CMAS_RESPONSE_TYPE_MONITOR = 4; // 0x4
- field public static final int CMAS_RESPONSE_TYPE_NONE = 7; // 0x7
- field public static final int CMAS_RESPONSE_TYPE_PREPARE = 2; // 0x2
- field public static final int CMAS_RESPONSE_TYPE_SHELTER = 0; // 0x0
- field public static final int CMAS_RESPONSE_TYPE_UNKNOWN = -1; // 0xffffffff
- field public static final int CMAS_SEVERITY_EXTREME = 0; // 0x0
- field public static final int CMAS_SEVERITY_SEVERE = 1; // 0x1
- field public static final int CMAS_SEVERITY_UNKNOWN = -1; // 0xffffffff
- field public static final int CMAS_URGENCY_EXPECTED = 1; // 0x1
- field public static final int CMAS_URGENCY_IMMEDIATE = 0; // 0x0
- field public static final int CMAS_URGENCY_UNKNOWN = -1; // 0xffffffff
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SmsCbCmasInfo> CREATOR;
- }
-
- public final class SmsCbEtwsInfo implements android.os.Parcelable {
- ctor public SmsCbEtwsInfo(int, boolean, boolean, boolean, @Nullable byte[]);
- method public int describeContents();
- method @Nullable public byte[] getPrimaryNotificationSignature();
- method public long getPrimaryNotificationTimestamp();
- method public int getWarningType();
- method public boolean isEmergencyUserAlert();
- method public boolean isPopupAlert();
- method public boolean isPrimary();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SmsCbEtwsInfo> CREATOR;
- field public static final int ETWS_WARNING_TYPE_EARTHQUAKE = 0; // 0x0
- field public static final int ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI = 2; // 0x2
- field public static final int ETWS_WARNING_TYPE_OTHER_EMERGENCY = 4; // 0x4
- field public static final int ETWS_WARNING_TYPE_TEST_MESSAGE = 3; // 0x3
- field public static final int ETWS_WARNING_TYPE_TSUNAMI = 1; // 0x1
- field public static final int ETWS_WARNING_TYPE_UNKNOWN = -1; // 0xffffffff
- }
-
- public final class SmsCbLocation implements android.os.Parcelable {
- ctor public SmsCbLocation(@NonNull String, int, int);
- method public int describeContents();
- method public int getCid();
- method public int getLac();
- method @NonNull public String getPlmn();
- method public boolean isInLocationArea(@NonNull android.telephony.SmsCbLocation);
- method public boolean isInLocationArea(@Nullable String, int, int);
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SmsCbLocation> CREATOR;
- }
-
- public final class SmsCbMessage implements android.os.Parcelable {
- ctor public SmsCbMessage(int, int, int, @NonNull android.telephony.SmsCbLocation, int, @Nullable String, int, @Nullable String, int, @Nullable android.telephony.SmsCbEtwsInfo, @Nullable android.telephony.SmsCbCmasInfo, int, @Nullable java.util.List<android.telephony.CbGeoUtils.Geometry>, long, int, int);
- method @NonNull public static android.telephony.SmsCbMessage createFromCursor(@NonNull android.database.Cursor);
- method public int describeContents();
- method @Nullable public android.telephony.SmsCbCmasInfo getCmasWarningInfo();
- method @NonNull public android.content.ContentValues getContentValues();
- method public int getDataCodingScheme();
- method @Nullable public android.telephony.SmsCbEtwsInfo getEtwsWarningInfo();
- method public int getGeographicalScope();
- method @NonNull public java.util.List<android.telephony.CbGeoUtils.Geometry> getGeometries();
- method @Nullable public String getLanguageCode();
- method @NonNull public android.telephony.SmsCbLocation getLocation();
- method public int getMaximumWaitingDuration();
- method @Nullable public String getMessageBody();
- method public int getMessageFormat();
- method public int getMessagePriority();
- method public long getReceivedTime();
- method public int getSerialNumber();
- method public int getServiceCategory();
- method public int getSlotIndex();
- method public int getSubscriptionId();
- method public boolean isCmasMessage();
- method public boolean isEmergencyMessage();
- method public boolean isEtwsMessage();
- method public boolean needGeoFencingCheck();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SmsCbMessage> CREATOR;
- field public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE = 3; // 0x3
- field public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE = 0; // 0x0
- field public static final int GEOGRAPHICAL_SCOPE_LOCATION_AREA_WIDE = 2; // 0x2
- field public static final int GEOGRAPHICAL_SCOPE_PLMN_WIDE = 1; // 0x1
- field public static final int MAXIMUM_WAIT_TIME_NOT_SET = 255; // 0xff
- field public static final int MESSAGE_FORMAT_3GPP = 1; // 0x1
- field public static final int MESSAGE_FORMAT_3GPP2 = 2; // 0x2
- field public static final int MESSAGE_PRIORITY_EMERGENCY = 3; // 0x3
- field public static final int MESSAGE_PRIORITY_INTERACTIVE = 1; // 0x1
- field public static final int MESSAGE_PRIORITY_NORMAL = 0; // 0x0
- field public static final int MESSAGE_PRIORITY_URGENT = 2; // 0x2
- }
-
- public final class SmsManager {
- method public boolean disableCellBroadcastRange(int, int, int);
- method public boolean enableCellBroadcastRange(int, int, int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getPremiumSmsConsent(@NonNull String);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void sendMultipartTextMessageWithoutPersisting(String, String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPremiumSmsConsent(@NonNull String, int);
- field public static final int PREMIUM_SMS_CONSENT_ALWAYS_ALLOW = 3; // 0x3
- field public static final int PREMIUM_SMS_CONSENT_ASK_USER = 1; // 0x1
- field public static final int PREMIUM_SMS_CONSENT_NEVER_ALLOW = 2; // 0x2
- field public static final int PREMIUM_SMS_CONSENT_UNKNOWN = 0; // 0x0
- }
-
- public class SmsMessage {
- method @Nullable public static android.telephony.SmsMessage createFromNativeSmsSubmitPdu(@NonNull byte[], boolean);
- method @Nullable public static android.telephony.SmsMessage.SubmitPdu getSmsPdu(int, int, @Nullable String, @NonNull String, @NonNull String, long);
- method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static byte[] getSubmitPduEncodedMessage(boolean, @NonNull String, @NonNull String, int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0, to=255) int, @IntRange(from=1, to=255) int, @IntRange(from=1, to=255) int);
- }
-
- public class SubscriptionInfo implements android.os.Parcelable {
- method public boolean areUiccApplicationsEnabled();
- method @Nullable public java.util.List<android.telephony.UiccAccessRule> getAccessRules();
- method public int getProfileClass();
- method public boolean isGroupDisabled();
- }
-
- public class SubscriptionManager {
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean canDisablePhysicalSubscription();
- method public boolean canManageSubscription(@NonNull android.telephony.SubscriptionInfo, @NonNull String);
- method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getActiveSubscriptionIdList();
- method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String);
- method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
- method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getCompleteActiveSubscriptionIdList();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
- method @NonNull public static android.content.res.Resources getResourcesForSubId(@NonNull android.content.Context, int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int);
- method public void requestEmbeddedSubscriptionInfoListRefresh();
- method public void requestEmbeddedSubscriptionInfoListRefresh(int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultDataSubId(int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultSmsSubId(int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPreferredDataSubscriptionId(int, boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setSubscriptionEnabled(int, boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUiccApplicationsEnabled(int, boolean);
- field @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS) public static final String ACTION_SUBSCRIPTION_PLANS_CHANGED = "android.telephony.action.SUBSCRIPTION_PLANS_CHANGED";
- field @NonNull public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
- field @Deprecated public static final int PROFILE_CLASS_DEFAULT;
- field public static final int PROFILE_CLASS_OPERATIONAL;
- field public static final int PROFILE_CLASS_PROVISIONING;
- field public static final int PROFILE_CLASS_TESTING;
- field public static final int PROFILE_CLASS_UNSET;
- field @NonNull public static final android.net.Uri VT_ENABLED_CONTENT_URI;
- field @NonNull public static final android.net.Uri WFC_ENABLED_CONTENT_URI;
- field @NonNull public static final android.net.Uri WFC_MODE_CONTENT_URI;
- field @NonNull public static final android.net.Uri WFC_ROAMING_ENABLED_CONTENT_URI;
- field @NonNull public static final android.net.Uri WFC_ROAMING_MODE_CONTENT_URI;
- }
-
- public final class TelephonyHistogram implements android.os.Parcelable {
- ctor public TelephonyHistogram(int, int, int);
- ctor public TelephonyHistogram(android.telephony.TelephonyHistogram);
- ctor public TelephonyHistogram(android.os.Parcel);
- method public void addTimeTaken(int);
- method public int describeContents();
- method public int getAverageTime();
- method public int getBucketCount();
- method public int[] getBucketCounters();
- method public int[] getBucketEndPoints();
- method public int getCategory();
- method public int getId();
- method public int getMaxTime();
- method public int getMinTime();
- method public int getSampleCount();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.TelephonyHistogram> CREATOR;
- field public static final int TELEPHONY_CATEGORY_RIL = 1; // 0x1
- }
-
- public class TelephonyManager {
- method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String);
- method public int checkCarrierPrivilegesForPackage(String);
- method public int checkCarrierPrivilegesForPackageAnyPhone(String);
- method public void dial(String);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean disableDataConnectivity();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean enableDataConnectivity();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean enableModemForSlot(int, boolean);
- method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void enableVideoCalling(boolean);
- method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getAidForAppType(int);
- method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getAllowedNetworkTypes();
- method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallForwarding(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CallForwardingInfoCallback);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallWaitingStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int);
- method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
- method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCarrierPrivilegeStatus(int);
- method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPrivilegedPackagesForAllActiveSubscriptions();
- method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaEnhancedRoamingIndicatorIconIndex();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(int);
- method public String getCdmaPrlVersion();
- method public int getCurrentPhoneType();
- method public int getCurrentPhoneType(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getDataActivationState();
- method @Deprecated public boolean getDataEnabled();
- method @Deprecated public boolean getDataEnabled(int);
- method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getDefaultRespondViaMessageApplication();
- method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceSoftwareVersion(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
- method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
- method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
- method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Map<java.lang.Integer,java.lang.Integer> getLogicalToPhysicalSlotMapping();
- method public int getMaxNumberOfSimultaneouslyActiveSims();
- method public static long getMaxNumberVerificationTimeoutMillis();
- method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getMergedImsisFromGroup();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getPreferredNetworkTypeBitmask();
- method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState();
- method public int getSimApplicationState();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimApplicationState(int);
- method public int getSimCardState();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimCardState(int);
- method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Locale getSimLocale();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getSupportedRadioAccessFamily();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.UiccSlotInfo[] getUiccSlotsInfo();
- method @Nullable public android.os.Bundle getVisualVoicemailSettings();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoiceActivationState();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handlePinMmi(String);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handlePinMmiForSubscriber(int, String);
- method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean iccCloseLogicalChannelBySlot(int, int);
- method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannelBySlot(int, @Nullable String, int);
- method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, @Nullable String);
- method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
- method public boolean isDataConnectivityPossible();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
- method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
- method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
- method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRadioOn();
- method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isTetheringApnRequired();
- method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isVideoCallingEnabled();
- method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean matchesCurrentSimOperator(@NonNull String, int, @Nullable String);
- method public boolean needsOtaServiceProvisioning();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean);
- method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetAllCarrierActions();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetCarrierKeysForImsiEncryption();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void resetIms(int);
- method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void resetOtaEmergencyNumberDbFilePath();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig();
- method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void resetSettings();
- method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAllowedNetworkTypes(long);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallForwarding(@NonNull android.telephony.CallForwardingInfo, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallWaitingEnabled(boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
- method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
- method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRadioEnabled(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadioPower(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerState(int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerStateForSlot(int, int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>);
- method @Deprecated public void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoiceActivationState(int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void shutdownAllRadios();
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean supplyPin(String);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPinReportResult(String);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean supplyPuk(String, String);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPukReportResult(String, String);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean switchSlots(int[]);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void toggleRadioOnOff();
- method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateOtaEmergencyNumberDbFilePath(@NonNull android.os.ParcelFileDescriptor);
- method public void updateServiceLocation();
- field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_ANOMALY_REPORTED = "android.telephony.action.ANOMALY_REPORTED";
- field public static final String ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED";
- field public static final String ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED";
- field public static final String ACTION_EMERGENCY_ASSISTANCE = "android.telephony.action.EMERGENCY_ASSISTANCE";
- field public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
- field public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED = "android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
- field public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE = "com.android.omadm.service.CONFIGURATION_UPDATE";
- field public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS = "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS";
- field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
- field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
- field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
- field public static final int CALL_WAITING_STATUS_DISABLED = 2; // 0x2
- field public static final int CALL_WAITING_STATUS_ENABLED = 1; // 0x1
- field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4
- field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3
- field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
- field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
- field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
- field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
- 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 public static final String EXTRA_PHONE_IN_ECM_STATE = "android.telephony.extra.PHONE_IN_ECM_STATE";
- field public static final String EXTRA_PHONE_IN_EMERGENCY_CALL = "android.telephony.extra.PHONE_IN_EMERGENCY_CALL";
- field public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
- field public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
- field public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
- field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
- field public static final int KEY_TYPE_EPDG = 1; // 0x1
- field public static final int KEY_TYPE_WLAN = 2; // 0x2
- field public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1; // 0x1
- field public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2; // 0x2
- field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
- field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
- field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
- field public static final long NETWORK_TYPE_BITMASK_EHRPD = 8192L; // 0x2000L
- field public static final long NETWORK_TYPE_BITMASK_EVDO_0 = 16L; // 0x10L
- field public static final long NETWORK_TYPE_BITMASK_EVDO_A = 32L; // 0x20L
- field public static final long NETWORK_TYPE_BITMASK_EVDO_B = 2048L; // 0x800L
- field public static final long NETWORK_TYPE_BITMASK_GPRS = 1L; // 0x1L
- field public static final long NETWORK_TYPE_BITMASK_GSM = 32768L; // 0x8000L
- field public static final long NETWORK_TYPE_BITMASK_HSDPA = 128L; // 0x80L
- field public static final long NETWORK_TYPE_BITMASK_HSPA = 512L; // 0x200L
- field public static final long NETWORK_TYPE_BITMASK_HSPAP = 16384L; // 0x4000L
- field public static final long NETWORK_TYPE_BITMASK_HSUPA = 256L; // 0x100L
- field public static final long NETWORK_TYPE_BITMASK_IWLAN = 131072L; // 0x20000L
- field public static final long NETWORK_TYPE_BITMASK_LTE = 4096L; // 0x1000L
- field public static final long NETWORK_TYPE_BITMASK_LTE_CA = 262144L; // 0x40000L
- field public static final long NETWORK_TYPE_BITMASK_NR = 524288L; // 0x80000L
- field public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = 65536L; // 0x10000L
- field public static final long NETWORK_TYPE_BITMASK_UMTS = 4L; // 0x4L
- field public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; // 0x0L
- field public static final int RADIO_POWER_OFF = 0; // 0x0
- field public static final int RADIO_POWER_ON = 1; // 0x1
- field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2
- field public static final int SET_CARRIER_RESTRICTION_ERROR = 2; // 0x2
- field public static final int SET_CARRIER_RESTRICTION_NOT_SUPPORTED = 1; // 0x1
- field public static final int SET_CARRIER_RESTRICTION_SUCCESS = 0; // 0x0
- field public static final int SIM_ACTIVATION_STATE_ACTIVATED = 2; // 0x2
- field public static final int SIM_ACTIVATION_STATE_ACTIVATING = 1; // 0x1
- field public static final int SIM_ACTIVATION_STATE_DEACTIVATED = 3; // 0x3
- field public static final int SIM_ACTIVATION_STATE_RESTRICTED = 4; // 0x4
- field public static final int SIM_ACTIVATION_STATE_UNKNOWN = 0; // 0x0
- field public static final int SIM_STATE_LOADED = 10; // 0xa
- field public static final int SIM_STATE_PRESENT = 11; // 0xb
- field public static final int SRVCC_STATE_HANDOVER_CANCELED = 3; // 0x3
- field public static final int SRVCC_STATE_HANDOVER_COMPLETED = 1; // 0x1
- field public static final int SRVCC_STATE_HANDOVER_FAILED = 2; // 0x2
- field public static final int SRVCC_STATE_HANDOVER_NONE = -1; // 0xffffffff
- field public static final int SRVCC_STATE_HANDOVER_STARTED = 0; // 0x0
- }
-
- public static interface TelephonyManager.CallForwardingInfoCallback {
- method public void onCallForwardingInfoAvailable(@NonNull android.telephony.CallForwardingInfo);
- method public void onError(int);
- field public static final int RESULT_ERROR_FDN_CHECK_FAILURE = 2; // 0x2
- field public static final int RESULT_ERROR_NOT_SUPPORTED = 3; // 0x3
- field public static final int RESULT_ERROR_UNKNOWN = 1; // 0x1
- field public static final int RESULT_SUCCESS = 0; // 0x0
- }
-
- public final class UiccAccessRule implements android.os.Parcelable {
- ctor public UiccAccessRule(byte[], @Nullable String, long);
- method public int describeContents();
- method public int getCarrierPrivilegeStatus(android.content.pm.PackageInfo);
- method public int getCarrierPrivilegeStatus(android.content.pm.Signature, String);
- method public String getCertificateHexString();
- method @Nullable public String getPackageName();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.UiccAccessRule> CREATOR;
- }
-
- public class UiccSlotInfo implements android.os.Parcelable {
- ctor @Deprecated public UiccSlotInfo(boolean, boolean, String, int, int, boolean);
- method public int describeContents();
- method public String getCardId();
- method public int getCardStateInfo();
- method public boolean getIsActive();
- method public boolean getIsEuicc();
- method public boolean getIsExtendedApduSupported();
- method public int getLogicalSlotIdx();
- method public boolean isRemovable();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int CARD_STATE_INFO_ABSENT = 1; // 0x1
- field public static final int CARD_STATE_INFO_ERROR = 3; // 0x3
- field public static final int CARD_STATE_INFO_PRESENT = 2; // 0x2
- field public static final int CARD_STATE_INFO_RESTRICTED = 4; // 0x4
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.UiccSlotInfo> CREATOR;
- }
-
- public abstract class VisualVoicemailService extends android.app.Service {
- method public static final void sendVisualVoicemailSms(android.content.Context, android.telecom.PhoneAccountHandle, String, short, String, android.app.PendingIntent);
- method public static final void setSmsFilterSettings(android.content.Context, android.telecom.PhoneAccountHandle, android.telephony.VisualVoicemailSmsFilterSettings);
- }
-
-}
-
-package android.telephony.cdma {
-
- public final class CdmaSmsCbProgramData implements android.os.Parcelable {
- method public int describeContents();
- method public int getCategory();
- method public int getOperation();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY = 4099; // 0x1003
- field public static final int CATEGORY_CMAS_EXTREME_THREAT = 4097; // 0x1001
- field public static final int CATEGORY_CMAS_LAST_RESERVED_VALUE = 4351; // 0x10ff
- field public static final int CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT = 4096; // 0x1000
- field public static final int CATEGORY_CMAS_SEVERE_THREAT = 4098; // 0x1002
- field public static final int CATEGORY_CMAS_TEST_MESSAGE = 4100; // 0x1004
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.cdma.CdmaSmsCbProgramData> CREATOR;
- field public static final int OPERATION_ADD_CATEGORY = 1; // 0x1
- field public static final int OPERATION_CLEAR_CATEGORIES = 2; // 0x2
- field public static final int OPERATION_DELETE_CATEGORY = 0; // 0x0
- }
-
-}
-
-package android.telephony.data {
-
- public final class DataCallResponse implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public java.util.List<android.net.LinkAddress> getAddresses();
- method public int getCause();
- method @NonNull public java.util.List<java.net.InetAddress> getDnsAddresses();
- method @NonNull public java.util.List<java.net.InetAddress> getGatewayAddresses();
- method public int getHandoverFailureMode();
- method public int getId();
- method @NonNull public String getInterfaceName();
- method public int getLinkStatus();
- method @Deprecated public int getMtu();
- method public int getMtuV4();
- method public int getMtuV6();
- method @NonNull public java.util.List<java.net.InetAddress> getPcscfAddresses();
- method public int getProtocolType();
- method public int getSuggestedRetryTime();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR;
- field public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 1; // 0x1
- field public static final int HANDOVER_FAILURE_MODE_LEGACY = 0; // 0x0
- field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER = 2; // 0x2
- field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 3; // 0x3
- field public static final int HANDOVER_FAILURE_MODE_UNKNOWN = -1; // 0xffffffff
- field public static final int LINK_STATUS_ACTIVE = 2; // 0x2
- field public static final int LINK_STATUS_DORMANT = 1; // 0x1
- field public static final int LINK_STATUS_INACTIVE = 0; // 0x0
- field public static final int LINK_STATUS_UNKNOWN = -1; // 0xffffffff
- }
-
- public static final class DataCallResponse.Builder {
- ctor public DataCallResponse.Builder();
- method @NonNull public android.telephony.data.DataCallResponse build();
- method @NonNull public android.telephony.data.DataCallResponse.Builder setAddresses(@NonNull java.util.List<android.net.LinkAddress>);
- method @NonNull public android.telephony.data.DataCallResponse.Builder setCause(int);
- method @NonNull public android.telephony.data.DataCallResponse.Builder setDnsAddresses(@NonNull java.util.List<java.net.InetAddress>);
- method @NonNull public android.telephony.data.DataCallResponse.Builder setGatewayAddresses(@NonNull java.util.List<java.net.InetAddress>);
- method @NonNull public android.telephony.data.DataCallResponse.Builder setHandoverFailureMode(int);
- method @NonNull public android.telephony.data.DataCallResponse.Builder setId(int);
- method @NonNull public android.telephony.data.DataCallResponse.Builder setInterfaceName(@NonNull String);
- method @NonNull public android.telephony.data.DataCallResponse.Builder setLinkStatus(int);
- method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setMtu(int);
- method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV4(int);
- method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV6(int);
- method @NonNull public android.telephony.data.DataCallResponse.Builder setPcscfAddresses(@NonNull java.util.List<java.net.InetAddress>);
- method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int);
- method @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int);
- }
-
- public final class DataProfile implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public String getApn();
- method public int getAuthType();
- method public int getBearerBitmask();
- method @Deprecated public int getMtu();
- method public int getMtuV4();
- method public int getMtuV6();
- method @Nullable public String getPassword();
- method public int getProfileId();
- method public int getProtocolType();
- method public int getRoamingProtocolType();
- method public int getSupportedApnTypesBitmask();
- method public int getType();
- method @Nullable public String getUserName();
- method public boolean isEnabled();
- method public boolean isPersistent();
- method public boolean isPreferred();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataProfile> CREATOR;
- field public static final int TYPE_3GPP = 1; // 0x1
- field public static final int TYPE_3GPP2 = 2; // 0x2
- field public static final int TYPE_COMMON = 0; // 0x0
- }
-
- public static final class DataProfile.Builder {
- ctor public DataProfile.Builder();
- method @NonNull public android.telephony.data.DataProfile build();
- method @NonNull public android.telephony.data.DataProfile.Builder enable(boolean);
- method @NonNull public android.telephony.data.DataProfile.Builder setApn(@NonNull String);
- method @NonNull public android.telephony.data.DataProfile.Builder setAuthType(int);
- method @NonNull public android.telephony.data.DataProfile.Builder setBearerBitmask(int);
- method @Deprecated @NonNull public android.telephony.data.DataProfile.Builder setMtu(int);
- method @NonNull public android.telephony.data.DataProfile.Builder setMtuV4(int);
- method @NonNull public android.telephony.data.DataProfile.Builder setMtuV6(int);
- method @NonNull public android.telephony.data.DataProfile.Builder setPassword(@NonNull String);
- method @NonNull public android.telephony.data.DataProfile.Builder setPersistent(boolean);
- method @NonNull public android.telephony.data.DataProfile.Builder setPreferred(boolean);
- method @NonNull public android.telephony.data.DataProfile.Builder setProfileId(int);
- method @NonNull public android.telephony.data.DataProfile.Builder setProtocolType(int);
- method @NonNull public android.telephony.data.DataProfile.Builder setRoamingProtocolType(int);
- method @NonNull public android.telephony.data.DataProfile.Builder setSupportedApnTypesBitmask(int);
- method @NonNull public android.telephony.data.DataProfile.Builder setType(int);
- method @NonNull public android.telephony.data.DataProfile.Builder setUserName(@NonNull String);
- }
-
- public abstract class DataService extends android.app.Service {
- ctor public DataService();
- method public android.os.IBinder onBind(android.content.Intent);
- method @Nullable public abstract android.telephony.data.DataService.DataServiceProvider onCreateDataServiceProvider(int);
- field public static final int REQUEST_REASON_HANDOVER = 3; // 0x3
- field public static final int REQUEST_REASON_NORMAL = 1; // 0x1
- field public static final int REQUEST_REASON_SHUTDOWN = 2; // 0x2
- field public static final int REQUEST_REASON_UNKNOWN = 0; // 0x0
- field public static final String SERVICE_INTERFACE = "android.telephony.data.DataService";
- }
-
- public abstract class DataService.DataServiceProvider implements java.lang.AutoCloseable {
- ctor public DataService.DataServiceProvider(int);
- method public abstract void close();
- method public void deactivateDataCall(int, int, @Nullable android.telephony.data.DataServiceCallback);
- method public final int getSlotIndex();
- method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
- method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback);
- method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
- method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback);
- method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback);
- }
-
- public class DataServiceCallback {
- method public void onDataCallListChanged(@NonNull java.util.List<android.telephony.data.DataCallResponse>);
- method public void onDeactivateDataCallComplete(int);
- method public void onRequestDataCallListComplete(int, @NonNull java.util.List<android.telephony.data.DataCallResponse>);
- method public void onSetDataProfileComplete(int);
- method public void onSetInitialAttachApnComplete(int);
- method public void onSetupDataCallComplete(int, @Nullable android.telephony.data.DataCallResponse);
- field public static final int RESULT_ERROR_BUSY = 3; // 0x3
- field public static final int RESULT_ERROR_ILLEGAL_STATE = 4; // 0x4
- field public static final int RESULT_ERROR_INVALID_ARG = 2; // 0x2
- field public static final int RESULT_ERROR_UNSUPPORTED = 1; // 0x1
- field public static final int RESULT_SUCCESS = 0; // 0x0
- }
-
- public abstract class QualifiedNetworksService extends android.app.Service {
- ctor public QualifiedNetworksService();
- method @NonNull public abstract android.telephony.data.QualifiedNetworksService.NetworkAvailabilityProvider onCreateNetworkAvailabilityProvider(int);
- field public static final String QUALIFIED_NETWORKS_SERVICE_INTERFACE = "android.telephony.data.QualifiedNetworksService";
- }
-
- public abstract class QualifiedNetworksService.NetworkAvailabilityProvider implements java.lang.AutoCloseable {
- ctor public QualifiedNetworksService.NetworkAvailabilityProvider(int);
- method public abstract void close();
- method public final int getSlotIndex();
- method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>);
- }
-
-}
-
-package android.telephony.euicc {
-
- public final class DownloadableSubscription implements android.os.Parcelable {
- method public java.util.List<android.telephony.UiccAccessRule> getAccessRules();
- method @Nullable public String getCarrierName();
- }
-
- public static final class DownloadableSubscription.Builder {
- ctor public DownloadableSubscription.Builder();
- ctor public DownloadableSubscription.Builder(android.telephony.euicc.DownloadableSubscription);
- method public android.telephony.euicc.DownloadableSubscription build();
- method public android.telephony.euicc.DownloadableSubscription.Builder setAccessRules(java.util.List<android.telephony.UiccAccessRule>);
- method public android.telephony.euicc.DownloadableSubscription.Builder setCarrierName(String);
- method public android.telephony.euicc.DownloadableSubscription.Builder setConfirmationCode(String);
- method public android.telephony.euicc.DownloadableSubscription.Builder setEncodedActivationCode(String);
- }
-
- public class EuiccCardManager {
- method public void authenticateServer(String, String, byte[], byte[], byte[], byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
- method public void cancelSession(String, byte[], @android.telephony.euicc.EuiccCardManager.CancelReason int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
- method public void deleteProfile(String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
- method public void disableProfile(String, String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
- method public void listNotifications(String, @android.telephony.euicc.EuiccNotification.Event int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification[]>);
- method public void loadBoundProfilePackage(String, byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
- method public void prepareDownload(String, @Nullable byte[], byte[], byte[], byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
- method public void removeNotificationFromList(String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
- method public void requestAllProfiles(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo[]>);
- method public void requestDefaultSmdpAddress(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.String>);
- method public void requestEuiccChallenge(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
- method public void requestEuiccInfo1(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
- method public void requestEuiccInfo2(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
- method public void requestProfile(String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>);
- method public void requestRulesAuthTable(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccRulesAuthTable>);
- method public void requestSmdsAddress(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.String>);
- method public void resetMemory(String, @android.telephony.euicc.EuiccCardManager.ResetOption int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
- method public void retrieveNotification(String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification>);
- method public void retrieveNotificationList(String, @android.telephony.euicc.EuiccNotification.Event int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification[]>);
- method public void setDefaultSmdpAddress(String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
- method public void setNickname(String, String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
- method public void switchToProfile(String, String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>);
- field public static final int CANCEL_REASON_END_USER_REJECTED = 0; // 0x0
- field public static final int CANCEL_REASON_POSTPONED = 1; // 0x1
- field public static final int CANCEL_REASON_PPR_NOT_ALLOWED = 3; // 0x3
- field public static final int CANCEL_REASON_TIMEOUT = 2; // 0x2
- field public static final int RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES = 2; // 0x2
- field public static final int RESET_OPTION_DELETE_OPERATIONAL_PROFILES = 1; // 0x1
- field public static final int RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS = 4; // 0x4
- field public static final int RESULT_CALLER_NOT_ALLOWED = -3; // 0xfffffffd
- field public static final int RESULT_EUICC_NOT_FOUND = -2; // 0xfffffffe
- field public static final int RESULT_OK = 0; // 0x0
- field public static final int RESULT_UNKNOWN_ERROR = -1; // 0xffffffff
- }
-
- @IntDef(prefix={"CANCEL_REASON_"}, value={android.telephony.euicc.EuiccCardManager.CANCEL_REASON_END_USER_REJECTED, android.telephony.euicc.EuiccCardManager.CANCEL_REASON_POSTPONED, android.telephony.euicc.EuiccCardManager.CANCEL_REASON_TIMEOUT, android.telephony.euicc.EuiccCardManager.CANCEL_REASON_PPR_NOT_ALLOWED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccCardManager.CancelReason {
- }
-
- @IntDef(flag=true, prefix={"RESET_OPTION_"}, value={android.telephony.euicc.EuiccCardManager.RESET_OPTION_DELETE_OPERATIONAL_PROFILES, android.telephony.euicc.EuiccCardManager.RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES, android.telephony.euicc.EuiccCardManager.RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccCardManager.ResetOption {
- }
-
- public static interface EuiccCardManager.ResultCallback<T> {
- method public void onComplete(int, T);
- }
-
- public class EuiccManager {
- method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void continueOperation(android.content.Intent, android.os.Bundle);
- method @Deprecated @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptions(@NonNull android.app.PendingIntent);
- method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptions(@android.telephony.euicc.EuiccCardManager.ResetOption int, @NonNull android.app.PendingIntent);
- method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDefaultDownloadableSubscriptionList(android.app.PendingIntent);
- method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent);
- method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public int getOtaStatus();
- method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public java.util.List<java.lang.String> getSupportedCountries();
- method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public java.util.List<java.lang.String> getUnsupportedCountries();
- method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public boolean isSupportedCountry(@NonNull String);
- method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void setSupportedCountries(@NonNull java.util.List<java.lang.String>);
- method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void setUnsupportedCountries(@NonNull java.util.List<java.lang.String>);
- field public static final String ACTION_DELETE_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED";
- field @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public static final String ACTION_OTA_STATUS_CHANGED = "android.telephony.euicc.action.OTA_STATUS_CHANGED";
- field public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
- field public static final String ACTION_RENAME_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.RENAME_SUBSCRIPTION_PRIVILEGED";
- field public static final String ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.TOGGLE_SUBSCRIPTION_PRIVILEGED";
- field public static final int EUICC_ACTIVATION_TYPE_ACCOUNT_REQUIRED = 4; // 0x4
- field public static final int EUICC_ACTIVATION_TYPE_BACKUP = 2; // 0x2
- field public static final int EUICC_ACTIVATION_TYPE_DEFAULT = 1; // 0x1
- field public static final int EUICC_ACTIVATION_TYPE_TRANSFER = 3; // 0x3
- field public static final int EUICC_OTA_FAILED = 2; // 0x2
- field public static final int EUICC_OTA_IN_PROGRESS = 1; // 0x1
- field public static final int EUICC_OTA_NOT_NEEDED = 4; // 0x4
- field public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5; // 0x5
- field public static final int EUICC_OTA_SUCCEEDED = 3; // 0x3
- field public static final String EXTRA_ACTIVATION_TYPE = "android.telephony.euicc.extra.ACTIVATION_TYPE";
- field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS";
- field public static final String EXTRA_ENABLE_SUBSCRIPTION = "android.telephony.euicc.extra.ENABLE_SUBSCRIPTION";
- field public static final String EXTRA_FORCE_PROVISION = "android.telephony.euicc.extra.FORCE_PROVISION";
- field public static final String EXTRA_FROM_SUBSCRIPTION_ID = "android.telephony.euicc.extra.FROM_SUBSCRIPTION_ID";
- field public static final String EXTRA_PHYSICAL_SLOT_ID = "android.telephony.euicc.extra.PHYSICAL_SLOT_ID";
- field public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.euicc.extra.SUBSCRIPTION_ID";
- field public static final String EXTRA_SUBSCRIPTION_NICKNAME = "android.telephony.euicc.extra.SUBSCRIPTION_NICKNAME";
- }
-
- @IntDef(prefix={"EUICC_OTA_"}, value={android.telephony.euicc.EuiccManager.EUICC_OTA_IN_PROGRESS, android.telephony.euicc.EuiccManager.EUICC_OTA_FAILED, android.telephony.euicc.EuiccManager.EUICC_OTA_SUCCEEDED, android.telephony.euicc.EuiccManager.EUICC_OTA_NOT_NEEDED, android.telephony.euicc.EuiccManager.EUICC_OTA_STATUS_UNAVAILABLE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccManager.OtaStatus {
- }
-
- public final class EuiccNotification implements android.os.Parcelable {
- ctor public EuiccNotification(int, String, @android.telephony.euicc.EuiccNotification.Event int, @Nullable byte[]);
- method public int describeContents();
- method @Nullable public byte[] getData();
- method @android.telephony.euicc.EuiccNotification.Event public int getEvent();
- method public int getSeq();
- method public String getTargetAddr();
- method public void writeToParcel(android.os.Parcel, int);
- field @android.telephony.euicc.EuiccNotification.Event public static final int ALL_EVENTS = 15; // 0xf
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.euicc.EuiccNotification> CREATOR;
- field public static final int EVENT_DELETE = 8; // 0x8
- field public static final int EVENT_DISABLE = 4; // 0x4
- field public static final int EVENT_ENABLE = 2; // 0x2
- field public static final int EVENT_INSTALL = 1; // 0x1
- }
-
- @IntDef(flag=true, prefix={"EVENT_"}, value={android.telephony.euicc.EuiccNotification.EVENT_INSTALL, android.telephony.euicc.EuiccNotification.EVENT_ENABLE, android.telephony.euicc.EuiccNotification.EVENT_DISABLE, android.telephony.euicc.EuiccNotification.EVENT_DELETE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccNotification.Event {
- }
-
- public final class EuiccRulesAuthTable implements android.os.Parcelable {
- method public int describeContents();
- method public int findIndex(@android.service.euicc.EuiccProfileInfo.PolicyRule int, android.service.carrier.CarrierIdentifier);
- method public boolean hasPolicyRuleFlag(int, @android.telephony.euicc.EuiccRulesAuthTable.PolicyRuleFlag int);
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.euicc.EuiccRulesAuthTable> CREATOR;
- field public static final int POLICY_RULE_FLAG_CONSENT_REQUIRED = 1; // 0x1
- }
-
- public static final class EuiccRulesAuthTable.Builder {
- ctor public EuiccRulesAuthTable.Builder(int);
- method public android.telephony.euicc.EuiccRulesAuthTable.Builder add(int, java.util.List<android.service.carrier.CarrierIdentifier>, int);
- method public android.telephony.euicc.EuiccRulesAuthTable build();
- }
-
- @IntDef(flag=true, prefix={"POLICY_RULE_FLAG_"}, value={android.telephony.euicc.EuiccRulesAuthTable.POLICY_RULE_FLAG_CONSENT_REQUIRED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccRulesAuthTable.PolicyRuleFlag {
- }
-
-}
-
-package android.telephony.ims {
-
- public final class ImsCallForwardInfo implements android.os.Parcelable {
- ctor public ImsCallForwardInfo(int, int, int, int, @NonNull String, int);
- method public int describeContents();
- method public int getCondition();
- method public String getNumber();
- method public int getServiceClass();
- method public int getStatus();
- method public int getTimeSeconds();
- method public int getToA();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int CDIV_CF_REASON_ALL = 4; // 0x4
- field public static final int CDIV_CF_REASON_ALL_CONDITIONAL = 5; // 0x5
- field public static final int CDIV_CF_REASON_BUSY = 1; // 0x1
- field public static final int CDIV_CF_REASON_NOT_LOGGED_IN = 6; // 0x6
- field public static final int CDIV_CF_REASON_NOT_REACHABLE = 3; // 0x3
- field public static final int CDIV_CF_REASON_NO_REPLY = 2; // 0x2
- field public static final int CDIV_CF_REASON_UNCONDITIONAL = 0; // 0x0
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsCallForwardInfo> CREATOR;
- field public static final int STATUS_ACTIVE = 1; // 0x1
- field public static final int STATUS_NOT_ACTIVE = 0; // 0x0
- field public static final int TYPE_OF_ADDRESS_INTERNATIONAL = 145; // 0x91
- field public static final int TYPE_OF_ADDRESS_UNKNOWN = 129; // 0x81
- }
-
- public final class ImsCallProfile implements android.os.Parcelable {
- ctor public ImsCallProfile();
- ctor public ImsCallProfile(int, int);
- ctor public ImsCallProfile(int, int, android.os.Bundle, android.telephony.ims.ImsStreamMediaProfile);
- method public int describeContents();
- method public String getCallExtra(String);
- method public String getCallExtra(String, String);
- method public boolean getCallExtraBoolean(String);
- method public boolean getCallExtraBoolean(String, boolean);
- method public int getCallExtraInt(String);
- method public int getCallExtraInt(String, int);
- method public android.os.Bundle getCallExtras();
- method public int getCallType();
- method public static int getCallTypeFromVideoState(int);
- method public int getCallerNumberVerificationStatus();
- method public int getEmergencyCallRouting();
- method public int getEmergencyServiceCategories();
- method @NonNull public java.util.List<java.lang.String> getEmergencyUrns();
- method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
- method @NonNull public android.os.Bundle getProprietaryCallExtras();
- method public int getRestrictCause();
- method public int getServiceType();
- method public static int getVideoStateFromCallType(int);
- method public static int getVideoStateFromImsCallProfile(android.telephony.ims.ImsCallProfile);
- method public boolean hasKnownUserIntentEmergency();
- method public boolean isEmergencyCallTesting();
- method public boolean isVideoCall();
- method public boolean isVideoPaused();
- method public static int presentationToOir(int);
- method public void setCallExtra(String, String);
- method public void setCallExtraBoolean(String, boolean);
- method public void setCallExtraInt(String, int);
- method public void setCallRestrictCause(int);
- method public void setCallerNumberVerificationStatus(int);
- method public void setEmergencyCallRouting(int);
- method public void setEmergencyCallTesting(boolean);
- method public void setEmergencyServiceCategories(int);
- method public void setEmergencyUrns(@NonNull java.util.List<java.lang.String>);
- method public void setHasKnownUserIntentEmergency(boolean);
- method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
- method public void updateCallType(android.telephony.ims.ImsCallProfile);
- method public void updateMediaProfile(android.telephony.ims.ImsCallProfile);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int CALL_RESTRICT_CAUSE_DISABLED = 2; // 0x2
- field public static final int CALL_RESTRICT_CAUSE_HD = 3; // 0x3
- field public static final int CALL_RESTRICT_CAUSE_NONE = 0; // 0x0
- field public static final int CALL_RESTRICT_CAUSE_RAT = 1; // 0x1
- field public static final int CALL_TYPE_VIDEO_N_VOICE = 3; // 0x3
- field public static final int CALL_TYPE_VOICE = 2; // 0x2
- field public static final int CALL_TYPE_VOICE_N_VIDEO = 1; // 0x1
- field public static final int CALL_TYPE_VS = 8; // 0x8
- field public static final int CALL_TYPE_VS_RX = 10; // 0xa
- field public static final int CALL_TYPE_VS_TX = 9; // 0x9
- field public static final int CALL_TYPE_VT = 4; // 0x4
- field public static final int CALL_TYPE_VT_NODIR = 7; // 0x7
- field public static final int CALL_TYPE_VT_RX = 6; // 0x6
- field public static final int CALL_TYPE_VT_TX = 5; // 0x5
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsCallProfile> CREATOR;
- field public static final int DIALSTRING_NORMAL = 0; // 0x0
- field public static final int DIALSTRING_SS_CONF = 1; // 0x1
- field public static final int DIALSTRING_USSD = 2; // 0x2
- field public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
- field public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS = "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
- field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telephony.ims.extra.CALL_DISCONNECT_CAUSE";
- field public static final String EXTRA_CALL_NETWORK_TYPE = "android.telephony.ims.extra.CALL_NETWORK_TYPE";
- field @Deprecated public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
- field public static final String EXTRA_CHILD_NUMBER = "ChildNum";
- field public static final String EXTRA_CNA = "cna";
- field public static final String EXTRA_CNAP = "cnap";
- field public static final String EXTRA_CODEC = "Codec";
- field public static final String EXTRA_DIALSTRING = "dialstring";
- field public static final String EXTRA_DISPLAY_TEXT = "DisplayText";
- field public static final String EXTRA_EMERGENCY_CALL = "e_call";
- field public static final String EXTRA_FORWARDED_NUMBER = "android.telephony.ims.extra.FORWARDED_NUMBER";
- field public static final String EXTRA_IS_CALL_PULL = "CallPull";
- field public static final String EXTRA_OI = "oi";
- field public static final String EXTRA_OIR = "oir";
- field public static final String EXTRA_REMOTE_URI = "remote_uri";
- field public static final String EXTRA_USSD = "ussd";
- field public static final int OIR_DEFAULT = 0; // 0x0
- field public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2; // 0x2
- field public static final int OIR_PRESENTATION_PAYPHONE = 4; // 0x4
- field public static final int OIR_PRESENTATION_RESTRICTED = 1; // 0x1
- field public static final int OIR_PRESENTATION_UNKNOWN = 3; // 0x3
- field public static final int SERVICE_TYPE_EMERGENCY = 2; // 0x2
- field public static final int SERVICE_TYPE_NONE = 0; // 0x0
- field public static final int SERVICE_TYPE_NORMAL = 1; // 0x1
- field public static final int VERIFICATION_STATUS_FAILED = 2; // 0x2
- field public static final int VERIFICATION_STATUS_NOT_VERIFIED = 0; // 0x0
- field public static final int VERIFICATION_STATUS_PASSED = 1; // 0x1
- }
-
- public class ImsCallSessionListener {
- method public void callQualityChanged(@NonNull android.telephony.CallQuality);
- method public void callSessionConferenceExtendFailed(android.telephony.ims.ImsReasonInfo);
- method public void callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
- method public void callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
- method public void callSessionConferenceStateUpdated(android.telephony.ims.ImsConferenceState);
- method @Deprecated public void callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo);
- method @Deprecated public void callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo);
- method public void callSessionHeld(android.telephony.ims.ImsCallProfile);
- method public void callSessionHoldFailed(android.telephony.ims.ImsReasonInfo);
- method public void callSessionHoldReceived(android.telephony.ims.ImsCallProfile);
- method public void callSessionInitiated(android.telephony.ims.ImsCallProfile);
- method public void callSessionInitiatedFailed(android.telephony.ims.ImsReasonInfo);
- method public void callSessionInviteParticipantsRequestDelivered();
- method public void callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo);
- method @Deprecated public void callSessionMayHandover(int, int);
- method public void callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase);
- method public void callSessionMergeFailed(android.telephony.ims.ImsReasonInfo);
- method public void callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
- method public void callSessionMultipartyStateChanged(boolean);
- method public void callSessionProgressing(android.telephony.ims.ImsStreamMediaProfile);
- method public void callSessionRemoveParticipantsRequestDelivered();
- method public void callSessionRemoveParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo);
- method public void callSessionResumeFailed(android.telephony.ims.ImsReasonInfo);
- method public void callSessionResumeReceived(android.telephony.ims.ImsCallProfile);
- method public void callSessionResumed(android.telephony.ims.ImsCallProfile);
- method public void callSessionRttAudioIndicatorChanged(@NonNull android.telephony.ims.ImsStreamMediaProfile);
- method public void callSessionRttMessageReceived(String);
- method public void callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile);
- method public void callSessionRttModifyResponseReceived(int);
- method public void callSessionSuppServiceReceived(android.telephony.ims.ImsSuppServiceNotification);
- method public void callSessionTerminated(android.telephony.ims.ImsReasonInfo);
- method public void callSessionTtyModeReceived(int);
- method public void callSessionUpdateFailed(android.telephony.ims.ImsReasonInfo);
- method public void callSessionUpdateReceived(android.telephony.ims.ImsCallProfile);
- method public void callSessionUpdated(android.telephony.ims.ImsCallProfile);
- method public void callSessionUssdMessageReceived(int, String);
- method public void onHandover(int, int, @Nullable android.telephony.ims.ImsReasonInfo);
- method public void onHandoverFailed(int, int, @NonNull android.telephony.ims.ImsReasonInfo);
- method public void onMayHandover(int, int);
- }
-
- public final class ImsConferenceState implements android.os.Parcelable {
- method public int describeContents();
- method public static int getConnectionStateForStatus(String);
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsConferenceState> CREATOR;
- field public static final String DISPLAY_TEXT = "display-text";
- field public static final String ENDPOINT = "endpoint";
- field public static final String SIP_STATUS_CODE = "sipstatuscode";
- field public static final String STATUS = "status";
- field public static final String STATUS_ALERTING = "alerting";
- field public static final String STATUS_CONNECTED = "connected";
- field public static final String STATUS_CONNECT_FAIL = "connect-fail";
- field public static final String STATUS_DIALING_IN = "dialing-in";
- field public static final String STATUS_DIALING_OUT = "dialing-out";
- field public static final String STATUS_DISCONNECTED = "disconnected";
- field public static final String STATUS_DISCONNECTING = "disconnecting";
- field public static final String STATUS_MUTED_VIA_FOCUS = "muted-via-focus";
- field public static final String STATUS_ON_HOLD = "on-hold";
- field public static final String STATUS_PENDING = "pending";
- field public static final String STATUS_SEND_ONLY = "sendonly";
- field public static final String STATUS_SEND_RECV = "sendrecv";
- field public static final String USER = "user";
- field public final java.util.HashMap<java.lang.String,android.os.Bundle> mParticipants;
- }
-
- public final class ImsException extends java.lang.Exception {
- ctor public ImsException(@Nullable String);
- ctor public ImsException(@Nullable String, int);
- ctor public ImsException(@Nullable String, int, @Nullable Throwable);
- }
-
- public final class ImsExternalCallState implements android.os.Parcelable {
- ctor public ImsExternalCallState(@NonNull String, @NonNull android.net.Uri, @Nullable android.net.Uri, boolean, int, int, boolean);
- method public int describeContents();
- method @NonNull public android.net.Uri getAddress();
- method public int getCallId();
- method public int getCallState();
- method public int getCallType();
- method @Nullable public android.net.Uri getLocalAddress();
- method public boolean isCallHeld();
- method public boolean isCallPullable();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int CALL_STATE_CONFIRMED = 1; // 0x1
- field public static final int CALL_STATE_TERMINATED = 2; // 0x2
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
- }
-
- public class ImsManager {
- method @NonNull public android.telephony.ims.SipDelegateManager getSipDelegateManager(int);
- }
-
- public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
- method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiRoamingModeSetting();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(int, int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(int, int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void isSupported(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>) throws android.telephony.ims.ImsException;
- method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException;
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSettingEnabled(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiModeSetting(int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiNonPersistent(boolean, int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingModeSetting(int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingSettingEnabled(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiSettingEnabled(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVtSettingEnabled(boolean);
- method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback);
- }
-
- @Deprecated public static class ImsMmTelManager.RegistrationCallback extends android.telephony.ims.RegistrationManager.RegistrationCallback {
- ctor @Deprecated public ImsMmTelManager.RegistrationCallback();
- }
-
- public final class ImsReasonInfo implements android.os.Parcelable {
- field public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED = "Forbidden. Not Authorized for Service";
- }
-
- public class ImsService extends android.app.Service {
- ctor public ImsService();
- method public android.telephony.ims.feature.MmTelFeature createMmTelFeature(int);
- method public android.telephony.ims.feature.RcsFeature createRcsFeature(int);
- method public void disableIms(int);
- method public void enableIms(int);
- method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int);
- method public long getImsServiceCapabilities();
- method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
- method @Nullable public android.telephony.ims.stub.SipTransportImplBase getSipTransport(int);
- method public final void onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) throws android.os.RemoteException;
- method public android.telephony.ims.stub.ImsFeatureConfiguration querySupportedImsFeatures();
- method public void readyForFeatureCreation();
- field public static final long CAPABILITY_SIP_DELEGATE_CREATION = 2L; // 0x2L
- }
-
- public final class ImsSsData implements android.os.Parcelable {
- ctor public ImsSsData(int, int, int, int, int);
- method public int describeContents();
- method @Nullable public java.util.List<android.telephony.ims.ImsCallForwardInfo> getCallForwardInfo();
- method public int getRequestType();
- method public int getResult();
- method public int getServiceClass();
- method public int getServiceType();
- method @NonNull public java.util.List<android.telephony.ims.ImsSsInfo> getSuppServiceInfo();
- method public int getTeleserviceType();
- method public boolean isTypeBarring();
- method public boolean isTypeCf();
- method public boolean isTypeClip();
- method public boolean isTypeClir();
- method public boolean isTypeColp();
- method public boolean isTypeColr();
- method public boolean isTypeCw();
- method public boolean isTypeIcb();
- method public boolean isTypeInterrogation();
- method public boolean isTypeUnConditional();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSsData> CREATOR;
- field public static final int RESULT_SUCCESS = 0; // 0x0
- field public static final int SERVICE_CLASS_DATA = 2; // 0x2
- field public static final int SERVICE_CLASS_DATA_CIRCUIT_ASYNC = 32; // 0x20
- field public static final int SERVICE_CLASS_DATA_CIRCUIT_SYNC = 16; // 0x10
- field public static final int SERVICE_CLASS_DATA_PACKET_ACCESS = 64; // 0x40
- field public static final int SERVICE_CLASS_DATA_PAD = 128; // 0x80
- field public static final int SERVICE_CLASS_FAX = 4; // 0x4
- field public static final int SERVICE_CLASS_NONE = 0; // 0x0
- field public static final int SERVICE_CLASS_SMS = 8; // 0x8
- field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
- field public static final int SS_ACTIVATION = 0; // 0x0
- field public static final int SS_ALL_BARRING = 18; // 0x12
- field public static final int SS_ALL_DATA_TELESERVICES = 3; // 0x3
- field public static final int SS_ALL_TELESERVICES_EXCEPT_SMS = 5; // 0x5
- field public static final int SS_ALL_TELESEVICES = 1; // 0x1
- field public static final int SS_ALL_TELE_AND_BEARER_SERVICES = 0; // 0x0
- field public static final int SS_BAIC = 16; // 0x10
- field public static final int SS_BAIC_ROAMING = 17; // 0x11
- field public static final int SS_BAOC = 13; // 0xd
- field public static final int SS_BAOIC = 14; // 0xe
- field public static final int SS_BAOIC_EXC_HOME = 15; // 0xf
- field public static final int SS_CFU = 0; // 0x0
- field public static final int SS_CFUT = 6; // 0x6
- field public static final int SS_CF_ALL = 4; // 0x4
- field public static final int SS_CF_ALL_CONDITIONAL = 5; // 0x5
- field public static final int SS_CF_BUSY = 1; // 0x1
- field public static final int SS_CF_NOT_REACHABLE = 3; // 0x3
- field public static final int SS_CF_NO_REPLY = 2; // 0x2
- field public static final int SS_CLIP = 7; // 0x7
- field public static final int SS_CLIR = 8; // 0x8
- field public static final int SS_CNAP = 11; // 0xb
- field public static final int SS_COLP = 9; // 0x9
- field public static final int SS_COLR = 10; // 0xa
- field public static final int SS_DEACTIVATION = 1; // 0x1
- field public static final int SS_ERASURE = 4; // 0x4
- field public static final int SS_INCOMING_BARRING = 20; // 0x14
- field public static final int SS_INCOMING_BARRING_ANONYMOUS = 22; // 0x16
- field public static final int SS_INCOMING_BARRING_DN = 21; // 0x15
- field public static final int SS_INTERROGATION = 2; // 0x2
- field public static final int SS_OUTGOING_BARRING = 19; // 0x13
- field public static final int SS_REGISTRATION = 3; // 0x3
- field public static final int SS_SMS_SERVICES = 4; // 0x4
- field public static final int SS_TELEPHONY = 2; // 0x2
- field public static final int SS_WAIT = 12; // 0xc
- }
-
- public static final class ImsSsData.Builder {
- ctor public ImsSsData.Builder(int, int, int, int, int);
- method @NonNull public android.telephony.ims.ImsSsData build();
- method @NonNull public android.telephony.ims.ImsSsData.Builder setCallForwardingInfo(@NonNull java.util.List<android.telephony.ims.ImsCallForwardInfo>);
- method @NonNull public android.telephony.ims.ImsSsData.Builder setSuppServiceInfo(@NonNull java.util.List<android.telephony.ims.ImsSsInfo>);
- }
-
- public final class ImsSsInfo implements android.os.Parcelable {
- ctor @Deprecated public ImsSsInfo(int, @Nullable String);
- method public int describeContents();
- method public int getClirInterrogationStatus();
- method public int getClirOutgoingState();
- method @Deprecated public String getIcbNum();
- method @Nullable public String getIncomingCommunicationBarringNumber();
- method public int getProvisionStatus();
- method public int getStatus();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int CLIR_OUTGOING_DEFAULT = 0; // 0x0
- field public static final int CLIR_OUTGOING_INVOCATION = 1; // 0x1
- field public static final int CLIR_OUTGOING_SUPPRESSION = 2; // 0x2
- field public static final int CLIR_STATUS_NOT_PROVISIONED = 0; // 0x0
- field public static final int CLIR_STATUS_PROVISIONED_PERMANENT = 1; // 0x1
- field public static final int CLIR_STATUS_TEMPORARILY_ALLOWED = 4; // 0x4
- field public static final int CLIR_STATUS_TEMPORARILY_RESTRICTED = 3; // 0x3
- field public static final int CLIR_STATUS_UNKNOWN = 2; // 0x2
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSsInfo> CREATOR;
- field public static final int DISABLED = 0; // 0x0
- field public static final int ENABLED = 1; // 0x1
- field public static final int NOT_REGISTERED = -1; // 0xffffffff
- field public static final int SERVICE_NOT_PROVISIONED = 0; // 0x0
- field public static final int SERVICE_PROVISIONED = 1; // 0x1
- field public static final int SERVICE_PROVISIONING_UNKNOWN = -1; // 0xffffffff
- }
-
- public static final class ImsSsInfo.Builder {
- ctor public ImsSsInfo.Builder(int);
- method @NonNull public android.telephony.ims.ImsSsInfo build();
- method @NonNull public android.telephony.ims.ImsSsInfo.Builder setClirInterrogationStatus(int);
- method @NonNull public android.telephony.ims.ImsSsInfo.Builder setClirOutgoingState(int);
- method @NonNull public android.telephony.ims.ImsSsInfo.Builder setIncomingCommunicationBarringNumber(@NonNull String);
- method @NonNull public android.telephony.ims.ImsSsInfo.Builder setProvisionStatus(int);
- }
-
- public final class ImsStreamMediaProfile implements android.os.Parcelable {
- ctor public ImsStreamMediaProfile(int, int, int, int, int);
- method public void copyFrom(android.telephony.ims.ImsStreamMediaProfile);
- method public int describeContents();
- method public int getAudioDirection();
- method public int getAudioQuality();
- method public int getRttMode();
- method public int getVideoDirection();
- method public int getVideoQuality();
- method public boolean isReceivingRttAudio();
- method public boolean isRttCall();
- method public void setReceivingRttAudio(boolean);
- method public void setRttMode(int);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int AUDIO_QUALITY_AMR = 1; // 0x1
- field public static final int AUDIO_QUALITY_AMR_WB = 2; // 0x2
- field public static final int AUDIO_QUALITY_EVRC = 4; // 0x4
- field public static final int AUDIO_QUALITY_EVRC_B = 5; // 0x5
- field public static final int AUDIO_QUALITY_EVRC_NW = 7; // 0x7
- field public static final int AUDIO_QUALITY_EVRC_WB = 6; // 0x6
- field public static final int AUDIO_QUALITY_EVS_FB = 20; // 0x14
- field public static final int AUDIO_QUALITY_EVS_NB = 17; // 0x11
- field public static final int AUDIO_QUALITY_EVS_SWB = 19; // 0x13
- field public static final int AUDIO_QUALITY_EVS_WB = 18; // 0x12
- field public static final int AUDIO_QUALITY_G711A = 13; // 0xd
- field public static final int AUDIO_QUALITY_G711AB = 15; // 0xf
- field public static final int AUDIO_QUALITY_G711U = 11; // 0xb
- field public static final int AUDIO_QUALITY_G722 = 14; // 0xe
- field public static final int AUDIO_QUALITY_G723 = 12; // 0xc
- field public static final int AUDIO_QUALITY_G729 = 16; // 0x10
- field public static final int AUDIO_QUALITY_GSM_EFR = 8; // 0x8
- field public static final int AUDIO_QUALITY_GSM_FR = 9; // 0x9
- field public static final int AUDIO_QUALITY_GSM_HR = 10; // 0xa
- field public static final int AUDIO_QUALITY_NONE = 0; // 0x0
- field public static final int AUDIO_QUALITY_QCELP13K = 3; // 0x3
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsStreamMediaProfile> CREATOR;
- field public static final int DIRECTION_INACTIVE = 0; // 0x0
- field public static final int DIRECTION_INVALID = -1; // 0xffffffff
- field public static final int DIRECTION_RECEIVE = 1; // 0x1
- field public static final int DIRECTION_SEND = 2; // 0x2
- field public static final int DIRECTION_SEND_RECEIVE = 3; // 0x3
- field public static final int RTT_MODE_DISABLED = 0; // 0x0
- field public static final int RTT_MODE_FULL = 1; // 0x1
- field public static final int VIDEO_QUALITY_NONE = 0; // 0x0
- field public static final int VIDEO_QUALITY_QCIF = 1; // 0x1
- field public static final int VIDEO_QUALITY_QVGA_LANDSCAPE = 2; // 0x2
- field public static final int VIDEO_QUALITY_QVGA_PORTRAIT = 4; // 0x4
- field public static final int VIDEO_QUALITY_VGA_LANDSCAPE = 8; // 0x8
- field public static final int VIDEO_QUALITY_VGA_PORTRAIT = 16; // 0x10
- }
-
- public final class ImsSuppServiceNotification implements android.os.Parcelable {
- ctor public ImsSuppServiceNotification(int, int, int, int, String, String[]);
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSuppServiceNotification> CREATOR;
- field public final int code;
- field public final String[] history;
- field public final int index;
- field public final int notificationType;
- field public final String number;
- field public final int type;
- }
-
- public class ImsUtListener {
- method public void onLineIdentificationSupplementaryServiceResponse(int, @NonNull android.telephony.ims.ImsSsInfo);
- method public void onSupplementaryServiceIndication(android.telephony.ims.ImsSsData);
- method public void onUtConfigurationCallBarringQueried(int, android.telephony.ims.ImsSsInfo[]);
- method public void onUtConfigurationCallForwardQueried(int, android.telephony.ims.ImsCallForwardInfo[]);
- method public void onUtConfigurationCallWaitingQueried(int, android.telephony.ims.ImsSsInfo[]);
- method @Deprecated public void onUtConfigurationQueried(int, android.os.Bundle);
- method public void onUtConfigurationQueryFailed(int, android.telephony.ims.ImsReasonInfo);
- method public void onUtConfigurationUpdateFailed(int, android.telephony.ims.ImsReasonInfo);
- method public void onUtConfigurationUpdated(int);
- field @Deprecated public static final String BUNDLE_KEY_CLIR = "queryClir";
- field @Deprecated public static final String BUNDLE_KEY_SSINFO = "imsSsInfo";
- }
-
- public abstract class ImsVideoCallProvider {
- ctor public ImsVideoCallProvider();
- method public void changeCallDataUsage(long);
- method public void changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities);
- method public void changePeerDimensions(int, int);
- method public void changeVideoQuality(int);
- method public void handleCallSessionEvent(int);
- method public abstract void onRequestCallDataUsage();
- method public abstract void onRequestCameraCapabilities();
- method public abstract void onSendSessionModifyRequest(android.telecom.VideoProfile, android.telecom.VideoProfile);
- method public abstract void onSendSessionModifyResponse(android.telecom.VideoProfile);
- method public abstract void onSetCamera(String);
- method public void onSetCamera(String, int);
- method public abstract void onSetDeviceOrientation(int);
- method public abstract void onSetDisplaySurface(android.view.Surface);
- method public abstract void onSetPauseImage(android.net.Uri);
- method public abstract void onSetPreviewSurface(android.view.Surface);
- method public abstract void onSetZoom(float);
- method public void receiveSessionModifyRequest(android.telecom.VideoProfile);
- method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
- }
-
- public class ProvisioningManager {
- method @NonNull public static android.telephony.ims.ProvisioningManager createForSubscriptionId(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public int getProvisioningIntValue(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(int, int);
- method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public String getProvisioningStringValue(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException;
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(int, int, boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
- field public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; // 0x43
- field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
- field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
- field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
- field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
- field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
- field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY";
- }
-
- public static class ProvisioningManager.Callback {
- ctor public ProvisioningManager.Callback();
- method public void onProvisioningIntChanged(int, int);
- method public void onProvisioningStringChanged(int, @NonNull String);
- }
-
- public class RcsUceAdapter {
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
- }
-
- public class SipDelegateManager {
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws android.telephony.ims.ImsException;
- }
-
-}
-
-package android.telephony.ims.feature {
-
- public final class CapabilityChangeRequest implements android.os.Parcelable {
- method public void addCapabilitiesToDisableForTech(int, int);
- method public void addCapabilitiesToEnableForTech(int, int);
- method public int describeContents();
- method public java.util.List<android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair> getCapabilitiesToDisable();
- method public java.util.List<android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair> getCapabilitiesToEnable();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.feature.CapabilityChangeRequest> CREATOR;
- }
-
- public static class CapabilityChangeRequest.CapabilityPair {
- ctor public CapabilityChangeRequest.CapabilityPair(int, int);
- method public int getCapability();
- method public int getRadioTech();
- }
-
- public abstract class ImsFeature {
- ctor public ImsFeature();
- method public abstract void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
- method public int getFeatureState();
- method public final int getSlotIndex();
- method public abstract void onFeatureReady();
- method public abstract void onFeatureRemoved();
- method public final void setFeatureState(int);
- field public static final int CAPABILITY_ERROR_GENERIC = -1; // 0xffffffff
- field public static final int CAPABILITY_SUCCESS = 0; // 0x0
- field public static final int FEATURE_EMERGENCY_MMTEL = 0; // 0x0
- field public static final int FEATURE_MMTEL = 1; // 0x1
- field public static final int FEATURE_RCS = 2; // 0x2
- field public static final int STATE_INITIALIZING = 1; // 0x1
- field public static final int STATE_READY = 2; // 0x2
- field public static final int STATE_UNAVAILABLE = 0; // 0x0
- }
-
- @Deprecated public static class ImsFeature.Capabilities {
- field @Deprecated protected int mCapabilities;
- }
-
- protected static class ImsFeature.CapabilityCallbackProxy {
- method public void onChangeCapabilityConfigurationError(int, int, int);
- }
-
- public class MmTelFeature extends android.telephony.ims.feature.ImsFeature {
- ctor public MmTelFeature();
- method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
- method @Nullable public android.telephony.ims.ImsCallProfile createCallProfile(int, int);
- method @Nullable public android.telephony.ims.stub.ImsCallSessionImplBase createCallSession(@NonNull android.telephony.ims.ImsCallProfile);
- method @NonNull public android.telephony.ims.stub.ImsEcbmImplBase getEcbm();
- method @NonNull public android.telephony.ims.stub.ImsMultiEndpointImplBase getMultiEndpoint();
- method @NonNull public android.telephony.ims.stub.ImsSmsImplBase getSmsImplementation();
- method @NonNull public android.telephony.ims.stub.ImsUtImplBase getUt();
- method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
- method public final void notifyIncomingCall(@NonNull android.telephony.ims.stub.ImsCallSessionImplBase, @NonNull android.os.Bundle);
- method public final void notifyRejectedCall(@NonNull android.telephony.ims.ImsCallProfile, @NonNull android.telephony.ims.ImsReasonInfo);
- method public final void notifyVoiceMessageCountUpdate(int);
- method public void onFeatureReady();
- method public void onFeatureRemoved();
- method public boolean queryCapabilityConfiguration(int, int);
- method @NonNull public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus();
- method public void setUiTtyMode(int, @Nullable android.os.Message);
- method public int shouldProcessCall(@NonNull String[]);
- field public static final String EXTRA_IS_UNKNOWN_CALL = "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
- field public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";
- field public static final int PROCESS_CALL_CSFB = 1; // 0x1
- field public static final int PROCESS_CALL_IMS = 0; // 0x0
- }
-
- public static class MmTelFeature.MmTelCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
- ctor public MmTelFeature.MmTelCapabilities();
- ctor @Deprecated public MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities);
- ctor public MmTelFeature.MmTelCapabilities(int);
- method public final void addCapabilities(int);
- method public final void removeCapabilities(int);
- }
-
- public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
- ctor public RcsFeature();
- method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
- method public void onFeatureReady();
- method public void onFeatureRemoved();
- }
-
-}
-
-package android.telephony.ims.stub {
-
- public class ImsCallSessionImplBase implements java.lang.AutoCloseable {
- ctor public ImsCallSessionImplBase();
- method public void accept(int, android.telephony.ims.ImsStreamMediaProfile);
- method public void close();
- method public void deflect(String);
- method public void extendToConference(String[]);
- method public String getCallId();
- method public android.telephony.ims.ImsCallProfile getCallProfile();
- method public android.telephony.ims.ImsVideoCallProvider getImsVideoCallProvider();
- method public android.telephony.ims.ImsCallProfile getLocalCallProfile();
- method public String getProperty(String);
- method public android.telephony.ims.ImsCallProfile getRemoteCallProfile();
- method public int getState();
- method public void hold(android.telephony.ims.ImsStreamMediaProfile);
- method public void inviteParticipants(String[]);
- method public boolean isInCall();
- method public boolean isMultiparty();
- method public void merge();
- method public void reject(int);
- method public void removeParticipants(String[]);
- method public void resume(android.telephony.ims.ImsStreamMediaProfile);
- method public void sendDtmf(char, android.os.Message);
- method public void sendRttMessage(String);
- method public void sendRttModifyRequest(android.telephony.ims.ImsCallProfile);
- method public void sendRttModifyResponse(boolean);
- method public void sendUssd(String);
- method public void setListener(android.telephony.ims.ImsCallSessionListener);
- method public void setMute(boolean);
- method public void start(String, android.telephony.ims.ImsCallProfile);
- method public void startConference(String[], android.telephony.ims.ImsCallProfile);
- method public void startDtmf(char);
- method public void stopDtmf();
- method public void terminate(int);
- method public void update(int, android.telephony.ims.ImsStreamMediaProfile);
- field public static final int USSD_MODE_NOTIFY = 0; // 0x0
- field public static final int USSD_MODE_REQUEST = 1; // 0x1
- }
-
- public static class ImsCallSessionImplBase.State {
- method public static String toString(int);
- field public static final int ESTABLISHED = 4; // 0x4
- field public static final int ESTABLISHING = 3; // 0x3
- field public static final int IDLE = 0; // 0x0
- field public static final int INITIATED = 1; // 0x1
- field public static final int INVALID = -1; // 0xffffffff
- field public static final int NEGOTIATING = 2; // 0x2
- field public static final int REESTABLISHING = 6; // 0x6
- field public static final int RENEGOTIATING = 5; // 0x5
- field public static final int TERMINATED = 8; // 0x8
- field public static final int TERMINATING = 7; // 0x7
- }
-
- public class ImsConfigImplBase {
- ctor public ImsConfigImplBase();
- method public int getConfigInt(int);
- method public String getConfigString(int);
- method public final void notifyProvisionedValueChanged(int, int);
- method public final void notifyProvisionedValueChanged(int, String);
- method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
- method public int setConfig(int, int);
- method public int setConfig(int, String);
- field public static final int CONFIG_RESULT_FAILED = 1; // 0x1
- field public static final int CONFIG_RESULT_SUCCESS = 0; // 0x0
- field public static final int CONFIG_RESULT_UNKNOWN = -1; // 0xffffffff
- }
-
- public class ImsEcbmImplBase {
- ctor public ImsEcbmImplBase();
- method public final void enteredEcbm();
- method public void exitEmergencyCallbackMode();
- method public final void exitedEcbm();
- }
-
- public final class ImsFeatureConfiguration implements android.os.Parcelable {
- method public int describeContents();
- method public java.util.Set<android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair> getServiceFeatures();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.stub.ImsFeatureConfiguration> CREATOR;
- }
-
- public static class ImsFeatureConfiguration.Builder {
- ctor public ImsFeatureConfiguration.Builder();
- method public android.telephony.ims.stub.ImsFeatureConfiguration.Builder addFeature(int, int);
- method public android.telephony.ims.stub.ImsFeatureConfiguration build();
- }
-
- public static final class ImsFeatureConfiguration.FeatureSlotPair {
- ctor public ImsFeatureConfiguration.FeatureSlotPair(int, int);
- field public final int featureType;
- field public final int slotId;
- }
-
- public class ImsMultiEndpointImplBase {
- ctor public ImsMultiEndpointImplBase();
- method public final void onImsExternalCallStateUpdate(java.util.List<android.telephony.ims.ImsExternalCallState>);
- method public void requestImsExternalCallStateInfo();
- }
-
- public class ImsRegistrationImplBase {
- ctor public ImsRegistrationImplBase();
- method public final void onDeregistered(android.telephony.ims.ImsReasonInfo);
- method public final void onRegistered(int);
- method public final void onRegistering(int);
- method public final void onSubscriberAssociatedUriChanged(android.net.Uri[]);
- method public final void onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo);
- field public static final int REGISTRATION_TECH_IWLAN = 1; // 0x1
- field public static final int REGISTRATION_TECH_LTE = 0; // 0x0
- field public static final int REGISTRATION_TECH_NONE = -1; // 0xffffffff
- }
-
- public class ImsSmsImplBase {
- ctor public ImsSmsImplBase();
- method public void acknowledgeSms(int, @IntRange(from=0, to=65535) int, int);
- method public void acknowledgeSmsReport(int, @IntRange(from=0, to=65535) int, int);
- method public String getSmsFormat();
- method public void onReady();
- method @Deprecated public final void onSendSmsResult(int, @IntRange(from=0, to=65535) int, int, int) throws java.lang.RuntimeException;
- method public final void onSendSmsResultError(int, @IntRange(from=0, to=65535) int, int, int, int) throws java.lang.RuntimeException;
- method public final void onSendSmsResultSuccess(int, @IntRange(from=0, to=65535) int) throws java.lang.RuntimeException;
- method public final void onSmsReceived(int, String, byte[]) throws java.lang.RuntimeException;
- method @Deprecated public final void onSmsStatusReportReceived(int, @IntRange(from=0, to=65535) int, String, byte[]) throws java.lang.RuntimeException;
- method public final void onSmsStatusReportReceived(int, String, byte[]) throws java.lang.RuntimeException;
- method public void sendSms(int, @IntRange(from=0, to=65535) int, String, String, boolean, byte[]);
- field public static final int DELIVER_STATUS_ERROR_GENERIC = 2; // 0x2
- field public static final int DELIVER_STATUS_ERROR_NO_MEMORY = 3; // 0x3
- field public static final int DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED = 4; // 0x4
- field public static final int DELIVER_STATUS_OK = 1; // 0x1
- field public static final int RESULT_NO_NETWORK_ERROR = -1; // 0xffffffff
- field public static final int SEND_STATUS_ERROR = 2; // 0x2
- field public static final int SEND_STATUS_ERROR_FALLBACK = 4; // 0x4
- field public static final int SEND_STATUS_ERROR_RETRY = 3; // 0x3
- field public static final int SEND_STATUS_OK = 1; // 0x1
- field public static final int STATUS_REPORT_STATUS_ERROR = 2; // 0x2
- field public static final int STATUS_REPORT_STATUS_OK = 1; // 0x1
- }
-
- public class ImsUtImplBase {
- ctor public ImsUtImplBase();
- method public void close();
- method public int queryCallBarring(int);
- method public int queryCallBarringForServiceClass(int, int);
- method public int queryCallForward(int, String);
- method public int queryCallWaiting();
- method public int queryClip();
- method public int queryClir();
- method public int queryColp();
- method public int queryColr();
- method public void setListener(android.telephony.ims.ImsUtListener);
- method public int transact(android.os.Bundle);
- method public int updateCallBarring(int, int, String[]);
- method public int updateCallBarringForServiceClass(int, int, String[], int);
- method public int updateCallForward(int, int, String, int, int);
- method public int updateCallWaiting(boolean, int);
- method public int updateClip(boolean);
- method public int updateClir(int);
- method public int updateColp(boolean);
- method public int updateColr(int);
- }
-
- public class SipTransportImplBase {
- ctor public SipTransportImplBase(@NonNull java.util.concurrent.Executor);
- }
-
-}
-
-package android.telephony.mbms {
-
- public static class DownloadRequest.Builder {
- method public android.telephony.mbms.DownloadRequest.Builder setServiceId(String);
- }
-
- public final class FileInfo implements android.os.Parcelable {
- ctor public FileInfo(android.net.Uri, String);
- }
-
- public final class FileServiceInfo extends android.telephony.mbms.ServiceInfo implements android.os.Parcelable {
- ctor public FileServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date, java.util.List<android.telephony.mbms.FileInfo>);
- }
-
- public class MbmsDownloadReceiver extends android.content.BroadcastReceiver {
- field public static final int RESULT_APP_NOTIFICATION_ERROR = 6; // 0x6
- field public static final int RESULT_BAD_TEMP_FILE_ROOT = 3; // 0x3
- field public static final int RESULT_DOWNLOAD_FINALIZATION_ERROR = 4; // 0x4
- field public static final int RESULT_INVALID_ACTION = 1; // 0x1
- field public static final int RESULT_MALFORMED_INTENT = 2; // 0x2
- field public static final int RESULT_OK = 0; // 0x0
- field public static final int RESULT_TEMP_FILE_GENERATION_ERROR = 5; // 0x5
- }
-
- public final class StreamingServiceInfo extends android.telephony.mbms.ServiceInfo implements android.os.Parcelable {
- ctor public StreamingServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date);
- }
-
- public final class UriPathPair implements android.os.Parcelable {
- method public int describeContents();
- method public android.net.Uri getContentUri();
- method public android.net.Uri getFilePathUri();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.mbms.UriPathPair> CREATOR;
- }
-
-}
-
-package android.telephony.mbms.vendor {
-
- public class MbmsDownloadServiceBase extends android.os.Binder implements android.os.IInterface {
- ctor public MbmsDownloadServiceBase();
- method public int addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException;
- method public int addServiceAnnouncement(int, @NonNull byte[]);
- method public int addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException;
- method public android.os.IBinder asBinder();
- method public int cancelDownload(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
- method public void dispose(int) throws android.os.RemoteException;
- method public int download(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
- method public int initialize(int, android.telephony.mbms.MbmsDownloadSessionCallback) throws android.os.RemoteException;
- method @NonNull public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads(int) throws android.os.RemoteException;
- method public void onAppCallbackDied(int, int);
- method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
- method public int removeProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException;
- method public int removeStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException;
- method public int requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException;
- method public int requestUpdateFileServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
- method public int resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
- method public int setTempFileRootDirectory(int, String) throws android.os.RemoteException;
- }
-
- public class MbmsGroupCallServiceBase extends android.app.Service {
- ctor public MbmsGroupCallServiceBase();
- method public void dispose(int) throws android.os.RemoteException;
- method public int initialize(@NonNull android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
- method public void onAppCallbackDied(int, int);
- method public android.os.IBinder onBind(android.content.Intent);
- method public int startGroupCall(int, long, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>, @NonNull android.telephony.mbms.GroupCallCallback);
- method public void stopGroupCall(int, long);
- method public void updateGroupCall(int, long, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>);
- }
-
- public class MbmsStreamingServiceBase extends android.os.Binder implements android.os.IInterface {
- ctor public MbmsStreamingServiceBase();
- method public android.os.IBinder asBinder();
- method public void dispose(int) throws android.os.RemoteException;
- method @Nullable public android.net.Uri getPlaybackUri(int, String) throws android.os.RemoteException;
- method public int initialize(android.telephony.mbms.MbmsStreamingSessionCallback, int) throws android.os.RemoteException;
- method public void onAppCallbackDied(int, int);
- method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
- method public int requestUpdateStreamingServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
- method public int startStreaming(int, String, android.telephony.mbms.StreamingServiceCallback) throws android.os.RemoteException;
- method public void stopStreaming(int, String) throws android.os.RemoteException;
- }
-
- public class VendorUtils {
- ctor public VendorUtils();
- method public static android.content.ComponentName getAppReceiverFromPackageName(android.content.Context, String);
- field public static final String ACTION_CLEANUP = "android.telephony.mbms.action.CLEANUP";
- field public static final String ACTION_DOWNLOAD_RESULT_INTERNAL = "android.telephony.mbms.action.DOWNLOAD_RESULT_INTERNAL";
- field public static final String ACTION_FILE_DESCRIPTOR_REQUEST = "android.telephony.mbms.action.FILE_DESCRIPTOR_REQUEST";
- field public static final String EXTRA_FD_COUNT = "android.telephony.mbms.extra.FD_COUNT";
- field public static final String EXTRA_FINAL_URI = "android.telephony.mbms.extra.FINAL_URI";
- field public static final String EXTRA_FREE_URI_LIST = "android.telephony.mbms.extra.FREE_URI_LIST";
- field public static final String EXTRA_PAUSED_LIST = "android.telephony.mbms.extra.PAUSED_LIST";
- field public static final String EXTRA_PAUSED_URI_LIST = "android.telephony.mbms.extra.PAUSED_URI_LIST";
- field public static final String EXTRA_SERVICE_ID = "android.telephony.mbms.extra.SERVICE_ID";
- field public static final String EXTRA_TEMP_FILES_IN_USE = "android.telephony.mbms.extra.TEMP_FILES_IN_USE";
- field public static final String EXTRA_TEMP_FILE_ROOT = "android.telephony.mbms.extra.TEMP_FILE_ROOT";
- field public static final String EXTRA_TEMP_LIST = "android.telephony.mbms.extra.TEMP_LIST";
- }
-
-}
-
diff --git a/telephony/api/system-removed.txt b/telephony/api/system-removed.txt
deleted file mode 100644
index ae46075..0000000
--- a/telephony/api/system-removed.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-// Signature format: 2.0
-package android.telephony {
-
- public class TelephonyManager {
- method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void answerRingingCall();
- method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public boolean endCall();
- method @Deprecated public void silenceRinger();
- }
-
-}
-
-package android.telephony.data {
-
- public final class DataCallResponse implements android.os.Parcelable {
- ctor public DataCallResponse(int, int, int, int, int, @Nullable String, @Nullable java.util.List<android.net.LinkAddress>, @Nullable java.util.List<java.net.InetAddress>, @Nullable java.util.List<java.net.InetAddress>, @Nullable java.util.List<java.net.InetAddress>, int);
- }
-
-}
-
diff --git a/telephony/framework-telephony-jarjar-rules.txt b/telephony/framework-telephony-jarjar-rules.txt
deleted file mode 100644
index e1bb901..0000000
--- a/telephony/framework-telephony-jarjar-rules.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-rule android.telephony.Annotation* android.telephony.framework.Annotation@1
-rule android.util.RecurrenceRule* android.telephony.RecurrenceRule@1
-rule com.android.i18n.phonenumbers.** com.android.telephony.framework.phonenumbers.@1
-rule com.android.internal.os.SomeArgs* android.telephony.SomeArgs@1
-rule com.android.internal.util.BitwiseInputStream* android.telephony.BitwiseInputStream@1
-rule com.android.internal.util.BitwiseOutputStream* android.telephony.BitwiseOutputStream@1
-rule com.android.internal.util.FunctionalUtils* android.telephony.FunctionalUtils@1
-rule com.android.internal.util.Preconditions* android.telephony.Preconditions@1
-rule com.android.internal.util.IndentingPrintWriter* android.telephony.IndentingPrintWriter@1
-rule com.android.internal.util.HexDump* android.telephony.HexDump@1
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 68c833c..58a01e9 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -16,6 +16,8 @@
package android.telephony;
+import static android.text.TextUtils.formatSimple;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
@@ -145,7 +147,7 @@
if (mNetworkId == CellInfo.UNAVAILABLE || mSystemId == CellInfo.UNAVAILABLE
|| mBasestationId == CellInfo.UNAVAILABLE) return;
- mGlobalCellId = String.format("%04x%04x%04x", mSystemId, mNetworkId, mBasestationId);
+ mGlobalCellId = formatSimple("%04x%04x%04x", mSystemId, mNetworkId, mBasestationId);
}
/**
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 849c613..cfb9099 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -16,6 +16,8 @@
package android.telephony;
+import static android.text.TextUtils.formatSimple;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -147,7 +149,7 @@
if (mLac == CellInfo.UNAVAILABLE || mCid == CellInfo.UNAVAILABLE) return;
- mGlobalCellId = plmn + String.format("%04x%04x", mLac, mCid);
+ mGlobalCellId = plmn + formatSimple("%04x%04x", mLac, mCid);
}
/**
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index e6279dc..bbfab43 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -16,6 +16,8 @@
package android.telephony;
+import static android.text.TextUtils.formatSimple;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -185,7 +187,7 @@
if (mCi == CellInfo.UNAVAILABLE) return;
- mGlobalCellId = plmn + String.format("%07x", mCi);
+ mGlobalCellId = plmn + formatSimple("%07x", mCi);
}
/**
diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java
index 3923c756..2d2420d 100644
--- a/telephony/java/android/telephony/CellIdentityNr.java
+++ b/telephony/java/android/telephony/CellIdentityNr.java
@@ -16,6 +16,8 @@
package android.telephony;
+import static android.text.TextUtils.formatSimple;
+
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -128,7 +130,7 @@
if (mNci == CellInfo.UNAVAILABLE_LONG) return;
- mGlobalCellId = plmn + String.format("%09x", mNci);
+ mGlobalCellId = plmn + formatSimple("%09x", mNci);
}
/**
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index e74b709..ec07d54 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -16,6 +16,8 @@
package android.telephony;
+import static android.text.TextUtils.formatSimple;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
@@ -156,7 +158,7 @@
if (mLac == CellInfo.UNAVAILABLE || mCid == CellInfo.UNAVAILABLE) return;
- mGlobalCellId = plmn + String.format("%04x%04x", mLac, mCid);
+ mGlobalCellId = plmn + formatSimple("%04x%04x", mLac, mCid);
}
/**
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 40cb27e..b04a51d 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -16,6 +16,8 @@
package android.telephony;
+import static android.text.TextUtils.formatSimple;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -155,7 +157,7 @@
if (mLac == CellInfo.UNAVAILABLE || mCid == CellInfo.UNAVAILABLE) return;
- mGlobalCellId = plmn + String.format("%04x%04x", mLac, mCid);
+ mGlobalCellId = plmn + formatSimple("%04x%04x", mLac, mCid);
}
/**
diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java
index e164c4b..ec6c25d 100644
--- a/telephony/java/android/telephony/ModemActivityInfo.java
+++ b/telephony/java/android/telephony/ModemActivityInfo.java
@@ -130,7 +130,7 @@
+ " mTimestamp=" + mTimestamp
+ " mSleepTimeMs=" + mSleepTimeMs
+ " mIdleTimeMs=" + mIdleTimeMs
- + " mTxTimeMs[]=" + mTxTimeMs
+ + " mTxTimeMs[]=" + Arrays.toString(mTxTimeMs)
+ " mRxTimeMs=" + mRxTimeMs
+ "}";
}
@@ -319,8 +319,6 @@
*
* @return {@code true} if this {@link ModemActivityInfo} record is valid,
* {@code false} otherwise.
- * TODO: remove usages of this outside Telephony by always returning a valid (or null) result
- * from telephony.
* @hide
*/
@TestApi
@@ -331,7 +329,9 @@
&& (getReceiveTimeMillis() >= 0) && !isEmpty());
}
- private boolean isEmpty() {
+ /** @hide */
+ @TestApi
+ public boolean isEmpty() {
boolean isTxPowerEmpty = mTxTimeMs == null || mTxTimeMs.length == 0
|| Arrays.stream(mTxTimeMs).allMatch((i) -> i == 0);
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 9223842..f8a200a 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -369,7 +369,6 @@
* Get the 5G NR connection state.
*
* @return the 5G NR connection state.
- * @hide
*/
public @NRState int getNrState() {
return mNrState;
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index d3fca3e..2b17de6 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -1804,6 +1804,7 @@
*
* {@hide}
*/
+ @UnsupportedAppUsage
@RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC)
public boolean deleteMessageFromIcc(int messageIndex) {
boolean success = false;
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 11667c8..2547392 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -16,6 +16,8 @@
package android.telephony;
+import static android.text.TextUtils.formatSimple;
+
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -423,7 +425,7 @@
// Set text size scaled by density
paint.setTextSize(TEXT_SIZE * metrics.density);
// Convert sim slot index to localized string
- final String index = String.format("%d", mSimSlotIndex + 1);
+ final String index = formatSimple("%d", mSimSlotIndex + 1);
final Rect textBound = new Rect();
paint.getTextBounds(index, 0, 1, textBound);
final float xOffset = (width / 2.f) - textBound.centerX();
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 9126387..aa68dcf 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -55,7 +55,9 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.OutcomeReceiver;
import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
@@ -167,6 +169,9 @@
*/
public static final String MODEM_ACTIVITY_RESULT_KEY = "controller_activity";
+ /** @hide */
+ public static final String EXCEPTION_RESULT_KEY = "exception";
+
/**
* The process name of the Phone app as well as many other apps that use this process name, such
* as settings and vendor components.
@@ -10415,26 +10420,149 @@
return null;
}
-
/**
- * Requests the modem activity info. The recipient will place the result
- * in `result`.
- * @param result The object on which the recipient will send the resulting
- * {@link android.telephony.ModemActivityInfo} object with key of
- * {@link #MODEM_ACTIVITY_RESULT_KEY}.
+ * Exception that may be supplied to the callback provided in {@link #requestModemActivityInfo}.
* @hide
*/
- public void requestModemActivityInfo(@NonNull ResultReceiver result) {
+ @SystemApi
+ public static class ModemActivityInfoException extends Exception {
+ /** Indicates that an unknown error occurred */
+ public static final int ERROR_UNKNOWN = 0;
+
+ /**
+ * Indicates that the modem or phone processes are not available (such as when the device
+ * is in airplane mode).
+ */
+ public static final int ERROR_PHONE_NOT_AVAILABLE = 1;
+
+ /**
+ * Indicates that the modem supplied an invalid instance of {@link ModemActivityInfo}
+ */
+ public static final int ERROR_INVALID_INFO_RECEIVED = 2;
+
+ /**
+ * Indicates that the modem encountered an internal failure when processing the request
+ * for activity info.
+ */
+ public static final int ERROR_MODEM_RESPONSE_ERROR = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"ERROR_"},
+ value = {
+ ERROR_UNKNOWN,
+ ERROR_PHONE_NOT_AVAILABLE,
+ ERROR_INVALID_INFO_RECEIVED,
+ ERROR_MODEM_RESPONSE_ERROR,
+ })
+ public @interface ModemActivityInfoError {}
+
+ private final int mErrorCode;
+
+ /** @hide */
+ public ModemActivityInfoException(@ModemActivityInfoError int errorCode) {
+ mErrorCode = errorCode;
+ }
+
+ public @ModemActivityInfoError int getErrorCode() {
+ return mErrorCode;
+ }
+
+ @Override
+ public String toString() {
+ switch (mErrorCode) {
+ case ERROR_UNKNOWN: return "ERROR_UNKNOWN";
+ case ERROR_PHONE_NOT_AVAILABLE: return "ERROR_PHONE_NOT_AVAILABLE";
+ case ERROR_INVALID_INFO_RECEIVED: return "ERROR_INVALID_INFO_RECEIVED";
+ case ERROR_MODEM_RESPONSE_ERROR: return "ERROR_MODEM_RESPONSE_ERROR";
+ default: return "UNDEFINED";
+ }
+ }
+ }
+
+ /**
+ * Requests the current modem activity info.
+ *
+ * The provided instance of {@link ModemActivityInfo} represents the cumulative activity since
+ * the last restart of the phone process.
+ *
+ * @param callback A callback object to which the result will be delivered. If there was an
+ * error processing the request, {@link OutcomeReceiver#onError} will be called
+ * with more details about the error.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void requestModemActivityInfo(@NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<ModemActivityInfo, ModemActivityInfoException> callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ // Pass no handler into the receiver, since we're going to be trampolining the call to the
+ // listener onto the provided executor.
+ ResultReceiver wrapperResultReceiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle data) {
+ if (data == null) {
+ Log.w(TAG, "requestModemActivityInfo: received null bundle");
+ sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN);
+ return;
+ }
+ data.setDefusable(true);
+ if (data.containsKey(EXCEPTION_RESULT_KEY)) {
+ int receivedErrorCode = data.getInt(EXCEPTION_RESULT_KEY);
+ sendErrorToListener(receivedErrorCode);
+ return;
+ }
+
+ if (!data.containsKey(MODEM_ACTIVITY_RESULT_KEY)) {
+ Log.w(TAG, "requestModemActivityInfo: Bundle did not contain expected key");
+ sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN);
+ return;
+ }
+ Parcelable receivedResult = data.getParcelable(MODEM_ACTIVITY_RESULT_KEY);
+ if (!(receivedResult instanceof ModemActivityInfo)) {
+ Log.w(TAG, "requestModemActivityInfo: Bundle contained something that wasn't "
+ + "a ModemActivityInfo.");
+ sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN);
+ return;
+ }
+ ModemActivityInfo modemActivityInfo = (ModemActivityInfo) receivedResult;
+ if (!modemActivityInfo.isValid()) {
+ Log.w(TAG, "requestModemActivityInfo: Received an invalid ModemActivityInfo");
+ sendErrorToListener(ModemActivityInfoException.ERROR_INVALID_INFO_RECEIVED);
+ return;
+ }
+ Log.d(TAG, "requestModemActivityInfo: Sending result to app: " + modemActivityInfo);
+ sendResultToListener(modemActivityInfo);
+ }
+
+ private void sendResultToListener(ModemActivityInfo info) {
+ Binder.withCleanCallingIdentity(() ->
+ executor.execute(() ->
+ callback.onResult(info)));
+ }
+
+ private void sendErrorToListener(int code) {
+ ModemActivityInfoException e = new ModemActivityInfoException(code);
+ Binder.withCleanCallingIdentity(() ->
+ executor.execute(() ->
+ callback.onError(e)));
+ }
+ };
+
try {
ITelephony service = getITelephony();
if (service != null) {
- service.requestModemActivityInfo(result);
+ service.requestModemActivityInfo(wrapperResultReceiver);
return;
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#getModemActivityInfo", e);
}
- result.send(0, null);
+ executor.execute(() -> callback.onError(
+ new ModemActivityInfoException(
+ ModemActivityInfoException.ERROR_PHONE_NOT_AVAILABLE)));
}
/**
@@ -13544,6 +13672,149 @@
}
}
+ /**
+ * No error. Operation succeeded.
+ * @hide
+ */
+ @SystemApi
+ public static final int ENABLE_NR_DUAL_CONNECTIVITY_SUCCESS = 0;
+
+ /**
+ * NR Dual connectivity enablement is not supported.
+ * @hide
+ */
+ @SystemApi
+ public static final int ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED = 1;
+
+ /**
+ * Radio is not available.
+ * @hide
+ */
+ @SystemApi
+ public static final int ENABLE_NR_DUAL_CONNECTIVITY_RADIO_NOT_AVAILABLE = 2;
+
+ /**
+ * Internal Radio error.
+ * @hide
+ */
+ @SystemApi
+ public static final int ENABLE_NR_DUAL_CONNECTIVITY_RADIO_ERROR = 3;
+
+ /**
+ * Currently in invalid state. Not able to process the request.
+ * @hide
+ */
+ @SystemApi
+ public static final int ENABLE_NR_DUAL_CONNECTIVITY_INVALID_STATE = 4;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"ENABLE_NR_DUAL_CONNECTIVITY"}, value = {
+ ENABLE_NR_DUAL_CONNECTIVITY_SUCCESS,
+ ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED,
+ ENABLE_NR_DUAL_CONNECTIVITY_INVALID_STATE,
+ ENABLE_NR_DUAL_CONNECTIVITY_RADIO_NOT_AVAILABLE,
+ ENABLE_NR_DUAL_CONNECTIVITY_RADIO_ERROR})
+ public @interface EnableNrDualConnectivityResult {}
+
+ /**
+ * Enable NR dual connectivity. Enabled state does not mean dual connectivity
+ * is active. It means device is allowed to connect to both primary and secondary.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int NR_DUAL_CONNECTIVITY_ENABLE = 1;
+
+ /**
+ * Disable NR dual connectivity. Disabled state does not mean the secondary cell is released.
+ * Modem will release it only if the current bearer is released to avoid radio link failure.
+ * @hide
+ */
+ @SystemApi
+ public static final int NR_DUAL_CONNECTIVITY_DISABLE = 2;
+
+ /**
+ * Disable NR dual connectivity and force the secondary cell to be released if dual connectivity
+ * was active. This will result in radio link failure.
+ * @hide
+ */
+ @SystemApi
+ public static final int NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE = 3;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "NR_DUAL_CONNECTIVITY_" }, value = {
+ NR_DUAL_CONNECTIVITY_ENABLE,
+ NR_DUAL_CONNECTIVITY_DISABLE,
+ NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NrDualConnectivityState {
+ }
+
+ /**
+ * Enable/Disable E-UTRA-NR Dual Connectivity.
+ *
+ * @param nrDualConnectivityState expected NR dual connectivity state
+ * This can be passed following states
+ * <ol>
+ * <li>Enable NR dual connectivity {@link #NR_DUAL_CONNECTIVITY_ENABLE}
+ * <li>Disable NR dual connectivity {@link #NR_DUAL_CONNECTIVITY_DISABLE}
+ * <li>Disable NR dual connectivity and force secondary cell to be released
+ * {@link #NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE}
+ * </ol>
+ * @return operation result.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * @throws IllegalStateException if the Telephony process is not currently available.
+ * @hide
+ */
+ @SystemApi
+ public @EnableNrDualConnectivityResult int setNrDualConnectivityState(
+ @NrDualConnectivityState int nrDualConnectivityState) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.setNrDualConnectivityState(getSubId(), nrDualConnectivityState);
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "setNrDualConnectivityState RemoteException", ex);
+ ex.rethrowFromSystemServer();
+ }
+
+ return ENABLE_NR_DUAL_CONNECTIVITY_INVALID_STATE;
+ }
+
+ /**
+ * Is E-UTRA-NR Dual Connectivity enabled.
+ * @return true if dual connectivity is enabled else false. Enabled state does not mean dual
+ * connectivity is active. It means the device is allowed to connect to both primary and
+ * secondary cell.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+ * @throws IllegalStateException if the Telephony process is not currently available.
+ * @hide
+ */
+ @SystemApi
+ public boolean isNrDualConnectivityEnabled() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.isNrDualConnectivityEnabled(getSubId());
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "isNRDualConnectivityEnabled RemoteException", ex);
+ ex.rethrowFromSystemServer();
+ }
+ return false;
+ }
+
private static class DeathRecipient implements IBinder.DeathRecipient {
@Override
public void binderDied() {
diff --git a/telephony/java/android/telephony/UiccAccessRule.java b/telephony/java/android/telephony/UiccAccessRule.java
index 12bb366..7773c0a 100644
--- a/telephony/java/android/telephony/UiccAccessRule.java
+++ b/telephony/java/android/telephony/UiccAccessRule.java
@@ -204,13 +204,21 @@
* {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_NO_ACCESS}.
*/
public int getCarrierPrivilegeStatus(Signature signature, String packageName) {
- // SHA-1 is for backward compatible support only, strongly discouraged for new use.
- byte[] certHash = getCertHash(signature, "SHA-1");
byte[] certHash256 = getCertHash(signature, "SHA-256");
- if (matches(certHash, packageName) || matches(certHash256, packageName)) {
+ // Check SHA-256 first as it's the new standard.
+ if (matches(certHash256, packageName)) {
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
}
+ // Then check SHA-1 for backward compatibility. This should be removed
+ // in the near future when GPD_SPE_068 fully replaces GPD_SPE_013.
+ if (this.mCertificateHash.length == 20) {
+ byte[] certHash = getCertHash(signature, "SHA-1");
+ if (matches(certHash, packageName)) {
+ return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+ }
+ }
+
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 0d8351d..d33835f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2216,4 +2216,20 @@
* does not exist on the SIM card.
*/
List<String> getEquivalentHomePlmns(int subId, String callingPackage, String callingFeatureId);
+
+ /**
+ * Enable/Disable E-UTRA-NR Dual Connectivity
+ * @return operation result. See TelephonyManager.EnableNrDualConnectivityResult for
+ * details
+ * @param subId the id of the subscription
+ * @param enable enable/disable dual connectivity
+ */
+ int setNrDualConnectivityState(int subId, int nrDualConnectivityState);
+
+ /**
+ * Is E-UTRA-NR Dual Connectivity enabled
+ * @param subId the id of the subscription
+ * @return true if dual connectivity is enabled else false
+ */
+ boolean isNrDualConnectivityEnabled(int subId);
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index d524299..953a292 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -494,6 +494,8 @@
int RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS = 210;
int RIL_REQUEST_GET_BARRING_INFO = 211;
int RIL_REQUEST_ENTER_SIM_DEPERSONALIZATION = 212;
+ int RIL_REQUEST_ENABLE_NR_DUAL_CONNECTIVITY = 213;
+ int RIL_REQUEST_IS_NR_DUAL_CONNECTIVITY_ENABLED = 214;
/* Responses begin */
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 8457039..6b974ff 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -30,7 +30,7 @@
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("statusBarWindowIsAlwaysVisible", enabled, bugId) {
+ all("statusBarWindowIsAlwaysVisible", bugId, enabled) {
this.showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE)
}
}
@@ -40,7 +40,7 @@
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("navBarWindowIsAlwaysVisible", enabled, bugId) {
+ all("navBarWindowIsAlwaysVisible", bugId, enabled) {
this.showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE)
}
}
@@ -56,7 +56,7 @@
val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
val endingBounds = WindowUtils.getDisplayBounds(endRotation)
if (allStates) {
- all("noUncoveredRegions", enabled, bugId) {
+ all("noUncoveredRegions", bugId, enabled) {
if (startingBounds == endingBounds) {
this.coversAtLeastRegion(startingBounds)
} else {
@@ -77,21 +77,43 @@
@JvmOverloads
fun LayersAssertion.navBarLayerIsAlwaysVisible(
+ rotatesScreen: Boolean = false,
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("navBarLayerIsAlwaysVisible", enabled, bugId) {
- this.showsLayer(NAVIGATION_BAR_WINDOW_TITLE)
+ if (rotatesScreen) {
+ all("navBarLayerIsAlwaysVisible", bugId, enabled) {
+ this.showsLayer(NAVIGATION_BAR_WINDOW_TITLE)
+ .then()
+ .hidesLayer(NAVIGATION_BAR_WINDOW_TITLE)
+ .then()
+ .showsLayer(NAVIGATION_BAR_WINDOW_TITLE)
+ }
+ } else {
+ all("navBarLayerIsAlwaysVisible", bugId, enabled) {
+ this.showsLayer(NAVIGATION_BAR_WINDOW_TITLE)
+ }
}
}
@JvmOverloads
fun LayersAssertion.statusBarLayerIsAlwaysVisible(
+ rotatesScreen: Boolean = false,
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("statusBarLayerIsAlwaysVisible", enabled, bugId) {
- this.showsLayer(STATUS_BAR_WINDOW_TITLE)
+ if (rotatesScreen) {
+ all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
+ this.showsLayer(STATUS_BAR_WINDOW_TITLE)
+ .then()
+ hidesLayer(STATUS_BAR_WINDOW_TITLE)
+ .then()
+ .showsLayer(STATUS_BAR_WINDOW_TITLE)
+ }
+ } else {
+ all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
+ this.showsLayer(STATUS_BAR_WINDOW_TITLE)
+ }
}
}
@@ -105,10 +127,10 @@
val startingPos = WindowUtils.getNavigationBarPosition(beginRotation)
val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
- start("navBarLayerRotatesAndScales_StartingPos", enabled, bugId) {
+ start("navBarLayerRotatesAndScales_StartingPos", bugId, enabled) {
this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
}
- end("navBarLayerRotatesAndScales_EndingPost", enabled, bugId) {
+ end("navBarLayerRotatesAndScales_EndingPost", bugId, enabled) {
this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, endingPos)
}
@@ -129,10 +151,10 @@
val startingPos = WindowUtils.getStatusBarPosition(beginRotation)
val endingPos = WindowUtils.getStatusBarPosition(endRotation)
- start("statusBarLayerRotatesScales_StartingPos", enabled, bugId) {
+ start("statusBarLayerRotatesScales_StartingPos", bugId, enabled) {
this.hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, startingPos)
}
- end("statusBarLayerRotatesScales_EndingPos", enabled, bugId) {
+ end("statusBarLayerRotatesScales_EndingPos", bugId, enabled) {
this.hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, endingPos)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index 7647802..6cc2e22 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -43,7 +43,7 @@
/**
* Test IME window closing back to app window transitions.
- * To run this test: `atest FlickerTests:CloseImeWindowToAppTest`
+ * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest`
*/
@Presubmit
@RequiresDevice
@@ -94,14 +94,14 @@
}
layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
allStates = false)
navBarLayerRotatesAndScales(configuration.startRotation,
Surface.ROTATION_0, bugId = 140855415)
statusBarLayerRotatesScales(configuration.startRotation,
Surface.ROTATION_0)
+ navBarLayerIsAlwaysVisible(enabled = false)
+ statusBarLayerIsAlwaysVisible(enabled = false)
imeLayerBecomesInvisible(bugId = 141458352)
imeAppLayerBecomesInvisible(testApp, bugId = 153739621)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 6cfb282..8d9881e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -100,14 +100,14 @@
}
layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
noUncoveredRegions(configuration.startRotation,
Surface.ROTATION_0, allStates = false)
navBarLayerRotatesAndScales(configuration.startRotation,
Surface.ROTATION_0, bugId = 140855415)
statusBarLayerRotatesScales(configuration.startRotation,
Surface.ROTATION_0)
+ navBarLayerIsAlwaysVisible(enabled = false)
+ statusBarLayerIsAlwaysVisible(enabled = false)
imeLayerBecomesInvisible(bugId = 153739621)
imeAppLayerBecomesInvisible(testApp, bugId = 153739621)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
index b2be54f..5798624 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
@@ -27,7 +27,7 @@
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("imeLayerBecomesVisible", enabled, bugId) {
+ all("imeLayerBecomesVisible", bugId, enabled) {
this.hidesLayer(IME_WINDOW_TITLE)
.then()
.showsLayer(IME_WINDOW_TITLE)
@@ -38,7 +38,7 @@
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("imeLayerBecomesInvisible", enabled, bugId) {
+ all("imeLayerBecomesInvisible", bugId, enabled) {
this.showsLayer(IME_WINDOW_TITLE)
.then()
.hidesLayer(IME_WINDOW_TITLE)
@@ -50,7 +50,7 @@
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("imeAppLayerIsAlwaysVisible", enabled, bugId) {
+ all("imeAppLayerIsAlwaysVisible", bugId, enabled) {
this.showsLayer(testApp.getPackage())
}
}
@@ -60,7 +60,7 @@
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("imeAppWindowIsAlwaysVisible", enabled, bugId) {
+ all("imeAppWindowIsAlwaysVisible", bugId, enabled) {
this.showsAppWindowOnTop(testApp.getPackage())
}
}
@@ -69,7 +69,7 @@
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("imeWindowBecomesInvisible", enabled, bugId) {
+ all("imeWindowBecomesInvisible", bugId, enabled) {
this.showsNonAppWindow(IME_WINDOW_TITLE)
.then()
.hidesNonAppWindow(IME_WINDOW_TITLE)
@@ -81,7 +81,7 @@
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("imeAppWindowBecomesInvisible", enabled, bugId) {
+ all("imeAppWindowBecomesInvisible", bugId, enabled) {
this.showsAppWindowOnTop(testApp.getPackage())
.then()
.appWindowNotOnTop(testApp.getPackage())
@@ -93,7 +93,7 @@
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("imeAppLayerBecomesInvisible", enabled, bugId) {
+ all("imeAppLayerBecomesInvisible", bugId, enabled) {
this.skipUntilFirstAssertion()
.showsLayer(testApp.getPackage())
.then()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
index 7e857f3..d31c4ba 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
@@ -24,7 +24,7 @@
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("wallpaperWindowBecomesInvisible", enabled, bugId) {
+ all("wallpaperWindowBecomesInvisible", bugId, enabled) {
this.showsBelowAppWindow("Wallpaper")
.then()
.hidesBelowAppWindow("Wallpaper")
@@ -36,7 +36,7 @@
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("appWindowReplacesLauncherAsTopWindow", enabled, bugId) {
+ all("appWindowReplacesLauncherAsTopWindow", bugId, enabled) {
this.showsAppWindowOnTop("Launcher")
.then()
.showsAppWindowOnTop("Snapshot", testApp.getPackage())
@@ -48,7 +48,7 @@
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("wallpaperLayerBecomesInvisible", enabled, bugId) {
+ all("wallpaperLayerBecomesInvisible", bugId, enabled) {
this.showsLayer("Wallpaper")
.then()
.replaceVisibleLayer("Wallpaper", testApp.getPackage())
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 1081414..ad23d9f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -87,6 +87,7 @@
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
+
appWindowReplacesLauncherAsTopWindow(testApp)
wallpaperWindowBecomesInvisible()
}
@@ -99,9 +100,9 @@
configuration.endRotation)
statusBarLayerRotatesScales(Surface.ROTATION_0,
configuration.endRotation)
- navBarLayerIsAlwaysVisible(
- enabled = configuration.endRotation == Surface.ROTATION_0)
+ navBarLayerIsAlwaysVisible(enabled = false)
statusBarLayerIsAlwaysVisible(enabled = false)
+
wallpaperLayerBecomesInvisible(testApp)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index 2061994..5886a61 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -91,6 +91,7 @@
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
+
appWindowReplacesLauncherAsTopWindow(testApp)
wallpaperWindowBecomesInvisible(enabled = false)
}
@@ -103,9 +104,9 @@
configuration.endRotation)
statusBarLayerRotatesScales(Surface.ROTATION_0,
configuration.endRotation)
- navBarLayerIsAlwaysVisible(
- enabled = configuration.endRotation == Surface.ROTATION_0)
+ navBarLayerIsAlwaysVisible(enabled = false)
statusBarLayerIsAlwaysVisible(enabled = false)
+
wallpaperLayerBecomesInvisible(testApp)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index b29fae3..d100383 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -173,7 +173,7 @@
}
}
- all("noUncoveredRegions"/*, bugId = 147659548*/) {
+ all("noUncoveredRegions", bugId = 147659548) {
if (startingBounds == endingBounds) {
this.coversAtLeastRegion(startingBounds)
} else {
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 9d35cbc..0e9f723 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -9,4 +9,5 @@
"android-support-test",
"ub-uiautomator",
],
+ test_suites: ["device-tests"],
}
diff --git a/tests/Input/AndroidManifest.xml b/tests/Input/AndroidManifest.xml
index 4195df7..20f564e 100644
--- a/tests/Input/AndroidManifest.xml
+++ b/tests/Input/AndroidManifest.xml
@@ -27,7 +27,6 @@
android:process=":externalProcess">
</activity>
-
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.test.input"
diff --git a/tests/Input/TEST_MAPPING b/tests/Input/TEST_MAPPING
new file mode 100644
index 0000000..15b2bfa
--- /dev/null
+++ b/tests/Input/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "InputTests"
+ }
+ ]
+}
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index e169312..11a83eb 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -30,6 +30,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
@@ -359,6 +360,33 @@
}
@Test
+ public void testOemPrivate() {
+ NetworkCapabilities nc = new NetworkCapabilities();
+ // By default OEM_PRIVATE is neither in the unwanted or required lists and the network is
+ // not restricted.
+ assertFalse(nc.hasUnwantedCapability(NET_CAPABILITY_OEM_PRIVATE));
+ assertFalse(nc.hasCapability(NET_CAPABILITY_OEM_PRIVATE));
+ nc.maybeMarkCapabilitiesRestricted();
+ assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+
+ // Adding OEM_PRIVATE to capability list should make network restricted.
+ nc.addCapability(NET_CAPABILITY_OEM_PRIVATE);
+ nc.addCapability(NET_CAPABILITY_INTERNET); // Combine with unrestricted capability.
+ nc.maybeMarkCapabilitiesRestricted();
+ assertTrue(nc.hasCapability(NET_CAPABILITY_OEM_PRIVATE));
+ assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+
+ // Now let's make request for OEM_PRIVATE network.
+ NetworkCapabilities nr = new NetworkCapabilities();
+ nr.addCapability(NET_CAPABILITY_OEM_PRIVATE);
+ nr.maybeMarkCapabilitiesRestricted();
+ assertTrue(nr.satisfiedByNetworkCapabilities(nc));
+
+ // Request fails for network with the default capabilities.
+ assertFalse(nr.satisfiedByNetworkCapabilities(new NetworkCapabilities()));
+ }
+
+ @Test
public void testUnwantedCapabilities() {
NetworkCapabilities network = new NetworkCapabilities();
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1aae6cb..4081346 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -147,6 +147,7 @@
import android.net.ConnectivityManager.TooManyRequestsException;
import android.net.ConnectivityThread;
import android.net.DataStallReportParcelable;
+import android.net.EthernetManager;
import android.net.IConnectivityDiagnosticsCallback;
import android.net.IDnsResolver;
import android.net.IIpConnectivityMetrics;
@@ -358,6 +359,7 @@
@Mock AppOpsManager mAppOpsManager;
@Mock TelephonyManager mTelephonyManager;
@Mock MockableSystemProperties mSystemProperties;
+ @Mock EthernetManager mEthernetManager;
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -445,6 +447,7 @@
if (Context.LOCATION_SERVICE.equals(name)) return mLocationManager;
if (Context.APP_OPS_SERVICE.equals(name)) return mAppOpsManager;
if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
+ if (Context.ETHERNET_SERVICE.equals(name)) return mEthernetManager;
return super.getSystemService(name);
}
@@ -1274,7 +1277,6 @@
doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
doReturn(mIpConnectivityMetrics).when(deps).getIpConnectivityMetrics();
doReturn(mBatteryStatsService).when(deps).getBatteryStatsService();
- doReturn(true).when(deps).hasService(Context.ETHERNET_SERVICE);
doAnswer(inv -> {
mPolicyTracker = new WrappedMultinetworkPolicyTracker(
inv.getArgument(0), inv.getArgument(1), inv.getArgument(2));
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 737665f..aeb142b 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -64,26 +64,6 @@
} catch (RemoteException e) {
fail("Unexpected remote exception");
}
-
- try {
- mWm.prepareAppTransition(0, false);
- fail("IWindowManager.prepareAppTransition did not throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- } catch (RemoteException e) {
- fail("Unexpected remote exception");
- }
-
- try {
- mWm.executeAppTransition();
- fail("IWindowManager.executeAppTransition did not throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- } catch (RemoteException e) {
- fail("Unexpected remote exception");
- }
}
@SmallTest
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
index da64402..28ff606 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists.py
@@ -15,7 +15,7 @@
# limitations under the License.
"""Generate API lists for non-SDK API enforcement."""
import argparse
-from collections import defaultdict
+from collections import defaultdict, namedtuple
import functools
import os
import re
@@ -54,16 +54,21 @@
FLAGS_API_LIST_SET = set(FLAGS_API_LIST)
ALL_FLAGS_SET = set(ALL_FLAGS)
-# Suffix used in command line args to express that only known and
-# otherwise unassigned entries should be assign the given flag.
+# Option specified after one of FLAGS_API_LIST to indicate that
+# only known and otherwise unassigned entries should be assign the
+# given flag.
# For example, the max-target-P list is checked in as it was in P,
# but signatures have changes since then. The flag instructs this
# script to skip any entries which do not exist any more.
-FLAG_IGNORE_CONFLICTS_SUFFIX = "-ignore-conflicts"
+FLAG_IGNORE_CONFLICTS = "ignore-conflicts"
-# Suffix used in command line args to express that all apis within a given set
-# of packages should be assign the given flag.
-FLAG_PACKAGES_SUFFIX = "-packages"
+# Option specified after one of FLAGS_API_LIST to express that all
+# apis within a given set of packages should be assign the given flag.
+FLAG_PACKAGES = "packages"
+
+# Option specified after one of FLAGS_API_LIST to indicate an extra
+# tag that should be added to the matching APIs.
+FLAG_TAG = "tag"
# Regex patterns of fields/methods used in serialization. These are
# considered public API despite being hidden.
@@ -86,6 +91,17 @@
IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api)
+class StoreOrderedOptions(argparse.Action):
+ """An argparse action that stores a number of option arguments in the order that
+ they were specified.
+ """
+ def __call__(self, parser, args, values, option_string = None):
+ items = getattr(args, self.dest, None)
+ if items is None:
+ items = []
+ items.append([option_string.lstrip('-'), values])
+ setattr(args, self.dest, items)
+
def get_args():
"""Parses command line arguments.
@@ -98,17 +114,19 @@
help='CSV files to be merged into output')
for flag in ALL_FLAGS:
- ignore_conflicts_flag = flag + FLAG_IGNORE_CONFLICTS_SUFFIX
- packages_flag = flag + FLAG_PACKAGES_SUFFIX
- parser.add_argument('--' + flag, dest=flag, nargs='*', default=[], metavar='TXT_FILE',
- help='lists of entries with flag "' + flag + '"')
- parser.add_argument('--' + ignore_conflicts_flag, dest=ignore_conflicts_flag, nargs='*',
- default=[], metavar='TXT_FILE',
- help='lists of entries with flag "' + flag +
- '". skip entry if missing or flag conflict.')
- parser.add_argument('--' + packages_flag, dest=packages_flag, nargs='*',
- default=[], metavar='TXT_FILE',
- help='lists of packages to be added to ' + flag + ' list')
+ parser.add_argument('--' + flag, dest='ordered_flags', metavar='TXT_FILE',
+ action=StoreOrderedOptions, help='lists of entries with flag "' + flag + '"')
+ parser.add_argument('--' + FLAG_IGNORE_CONFLICTS, dest='ordered_flags', nargs=0,
+ action=StoreOrderedOptions, help='Indicates that only known and otherwise unassigned '
+ 'entries should be assign the given flag. Must follow a list of entries and applies '
+ 'to the preceding such list.')
+ parser.add_argument('--' + FLAG_PACKAGES, dest='ordered_flags', nargs=0,
+ action=StoreOrderedOptions, help='Indicates that the previous list of entries '
+ 'is a list of packages. All members in those packages will be given the flag. '
+ 'Must follow a list of entries and applies to the preceding such list.')
+ parser.add_argument('--' + FLAG_TAG, dest='ordered_flags', nargs=1,
+ action=StoreOrderedOptions, help='Adds an extra tag to the previous list of entries. '
+ 'Must follow a list of entries and applies to the preceding such list.')
return parser.parse_args()
@@ -170,11 +188,10 @@
def _check_entries_set(self, keys_subset, source):
assert isinstance(keys_subset, set)
assert keys_subset.issubset(self._dict_keyset), (
- "Error processing: {}\n"
- "The following entries were unexpected:\n"
+ "Error: {} specifies signatures not present in code:\n"
"{}"
"Please visit go/hiddenapi for more information.").format(
- source, "".join(map(lambda x: " " + str(x), keys_subset - self._dict_keyset)))
+ source, "".join(map(lambda x: " " + str(x) + "\n", keys_subset - self._dict_keyset)))
def _check_flags_set(self, flags_subset, source):
assert isinstance(flags_subset, set)
@@ -258,7 +275,7 @@
flags.append(FLAG_SDK)
self._dict[csv[0]].update(flags)
- def assign_flag(self, flag, apis, source="<unknown>"):
+ def assign_flag(self, flag, apis, source="<unknown>", tag = None):
"""Assigns a flag to given subset of entries.
Args:
@@ -278,11 +295,44 @@
# Iterate over the API subset, find each entry in dict and assign the flag to it.
for api in apis:
self._dict[api].add(flag)
+ if tag:
+ self._dict[api].add(tag)
+
+
+FlagFile = namedtuple('FlagFile', ('flag', 'file', 'ignore_conflicts', 'packages', 'tag'))
+
+def parse_ordered_flags(ordered_flags):
+ r = []
+ currentflag, file, ignore_conflicts, packages, tag = None, None, False, False, None
+ for flag_value in ordered_flags:
+ flag, value = flag_value[0], flag_value[1]
+ if flag in ALL_FLAGS_SET:
+ if currentflag:
+ r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
+ ignore_conflicts, packages, tag = False, False, None
+ currentflag = flag
+ file = value
+ else:
+ if currentflag is None:
+ raise argparse.ArgumentError('--%s is only allowed after one of %s' % (
+ flag, ' '.join(['--%s' % f for f in ALL_FLAGS_SET])))
+ if flag == FLAG_IGNORE_CONFLICTS:
+ ignore_conflicts = True
+ elif flag == FLAG_PACKAGES:
+ packages = True
+ elif flag == FLAG_TAG:
+ tag = value[0]
+
+
+ if currentflag:
+ r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
+ return r
def main(argv):
# Parse arguments.
args = vars(get_args())
+ flagfiles = parse_ordered_flags(args['ordered_flags'])
# Initialize API->flags dictionary.
flags = FlagsDict()
@@ -300,28 +350,28 @@
flags.assign_flag(FLAG_SDK, flags.filter_apis(IS_SERIALIZATION))
# (2) Merge text files with a known flag into the dictionary.
- for flag in ALL_FLAGS:
- for filename in args[flag]:
- flags.assign_flag(flag, read_lines(filename), filename)
+ for info in flagfiles:
+ if (not info.ignore_conflicts) and (not info.packages):
+ flags.assign_flag(info.flag, read_lines(info.file), info.file, info.tag)
# Merge text files where conflicts should be ignored.
# This will only assign the given flag if:
# (a) the entry exists, and
# (b) it has not been assigned any other flag.
# Because of (b), this must run after all strict assignments have been performed.
- for flag in ALL_FLAGS:
- for filename in args[flag + FLAG_IGNORE_CONFLICTS_SUFFIX]:
- valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(filename))
- flags.assign_flag(flag, valid_entries, filename)
+ for info in flagfiles:
+ if info.ignore_conflicts:
+ valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(info.file))
+ flags.assign_flag(info.flag, valid_entries, filename, info.tag)
# All members in the specified packages will be assigned the appropriate flag.
- for flag in ALL_FLAGS:
- for filename in args[flag + FLAG_PACKAGES_SUFFIX]:
- packages_needing_list = set(read_lines(filename))
+ for info in flagfiles:
+ if info.packages:
+ packages_needing_list = set(read_lines(info.file))
should_add_signature_to_list = lambda sig,lists: extract_package(
sig) in packages_needing_list and not lists
valid_entries = flags.filter_apis(should_add_signature_to_list)
- flags.assign_flag(flag, valid_entries)
+ flags.assign_flag(info.flag, valid_entries, info.file, info.tag)
# Mark all remaining entries as blocked.
flags.assign_flag(FLAG_BLOCKED, flags.filter_apis(HAS_NO_API_LIST_ASSIGNED))
diff --git a/tools/powerstats/PowerStatsServiceProtoParser.java b/tools/powerstats/PowerStatsServiceProtoParser.java
index 8ab302a..76edd63 100644
--- a/tools/powerstats/PowerStatsServiceProtoParser.java
+++ b/tools/powerstats/PowerStatsServiceProtoParser.java
@@ -25,50 +25,95 @@
* is output to STDOUT in csv format.
*/
public class PowerStatsServiceProtoParser {
- private static void printRailInfo(PowerStatsServiceProto proto) {
+ private static void printEnergyMeterInfo(PowerStatsServiceMeterProto proto) {
String csvHeader = new String();
- for (int i = 0; i < proto.getRailInfoCount(); i++) {
- RailInfoProto railInfo = proto.getRailInfo(i);
- csvHeader += "Index" + ","
- + "Timestamp" + ","
- + railInfo.getRailName() + "/" + railInfo.getSubsysName() + ",";
+ for (int i = 0; i < proto.getChannelInfoCount(); i++) {
+ ChannelInfoProto energyMeterInfo = proto.getChannelInfo(i);
+ csvHeader += "Index,Timestamp," + energyMeterInfo.getChannelId()
+ + "/" + energyMeterInfo.getChannelName() + ",";
}
System.out.println(csvHeader);
}
- private static void printEnergyData(PowerStatsServiceProto proto) {
- int railInfoCount = proto.getRailInfoCount();
+ private static void printEnergyMeasurements(PowerStatsServiceMeterProto proto) {
+ int energyMeterInfoCount = proto.getChannelInfoCount();
- if (railInfoCount > 0) {
- int energyDataCount = proto.getEnergyDataCount();
- int energyDataSetCount = energyDataCount / railInfoCount;
+ if (energyMeterInfoCount > 0) {
+ int energyMeasurementCount = proto.getEnergyMeasurementCount();
+ int energyMeasurementSetCount = energyMeasurementCount / energyMeterInfoCount;
- for (int i = 0; i < energyDataSetCount; i++) {
+ for (int i = 0; i < energyMeasurementSetCount; i++) {
String csvRow = new String();
- for (int j = 0; j < railInfoCount; j++) {
- EnergyDataProto energyData = proto.getEnergyData(i * railInfoCount + j);
- csvRow += energyData.getIndex() + ","
- + energyData.getTimestampMs() + ","
- + energyData.getEnergyUws() + ",";
+ for (int j = 0; j < energyMeterInfoCount; j++) {
+ EnergyMeasurementProto energyMeasurement =
+ proto.getEnergyMeasurement(i * energyMeterInfoCount + j);
+ csvRow += energyMeasurement.getChannelId() + ","
+ + energyMeasurement.getTimestampMs() + ","
+ + energyMeasurement.getEnergyUws() + ",";
}
System.out.println(csvRow);
}
} else {
- System.out.println("Error: railInfoCount is zero");
+ System.out.println("Error: energyMeterInfoCount is zero");
+ }
+ }
+
+ private static void printEnergyConsumerId(PowerStatsServiceModelProto proto) {
+ String csvHeader = new String();
+ for (int i = 0; i < proto.getEnergyConsumerIdCount(); i++) {
+ EnergyConsumerIdProto energyConsumerId = proto.getEnergyConsumerId(i);
+ csvHeader += "Index,Timestamp," + energyConsumerId.getEnergyConsumerId() + ",";
+ }
+ System.out.println(csvHeader);
+ }
+
+ private static void printEnergyConsumerResults(PowerStatsServiceModelProto proto) {
+ int energyConsumerIdCount = proto.getEnergyConsumerIdCount();
+
+ if (energyConsumerIdCount > 0) {
+ int energyConsumerResultCount = proto.getEnergyConsumerResultCount();
+ int energyConsumerResultSetCount = energyConsumerResultCount / energyConsumerIdCount;
+
+ for (int i = 0; i < energyConsumerResultSetCount; i++) {
+ String csvRow = new String();
+ for (int j = 0; j < energyConsumerIdCount; j++) {
+ EnergyConsumerResultProto energyConsumerResult =
+ proto.getEnergyConsumerResult(i * energyConsumerIdCount + j);
+ csvRow += energyConsumerResult.getEnergyConsumerId() + ","
+ + energyConsumerResult.getTimestampMs() + ","
+ + energyConsumerResult.getEnergyUws() + ",";
+ }
+ System.out.println(csvRow);
+ }
+ } else {
+ System.out.println("Error: energyConsumerIdCount is zero");
}
}
private static void generateCsvFile(String pathToIncidentReport) {
try {
- IncidentReportProto irProto =
- IncidentReportProto.parseFrom(new FileInputStream(pathToIncidentReport));
+ // Print power meter data.
+ IncidentReportMeterProto irMeterProto =
+ IncidentReportMeterProto.parseFrom(new FileInputStream(pathToIncidentReport));
- if (irProto.hasIncidentReport()) {
- PowerStatsServiceProto pssProto = irProto.getIncidentReport();
- printRailInfo(pssProto);
- printEnergyData(pssProto);
+ if (irMeterProto.hasIncidentReport()) {
+ PowerStatsServiceMeterProto pssMeterProto = irMeterProto.getIncidentReport();
+ printEnergyMeterInfo(pssMeterProto);
+ printEnergyMeasurements(pssMeterProto);
} else {
- System.out.println("Incident report not found. Exiting.");
+ System.out.println("Meter incident report not found. Exiting.");
+ }
+
+ // Print power model data.
+ IncidentReportModelProto irModelProto =
+ IncidentReportModelProto.parseFrom(new FileInputStream(pathToIncidentReport));
+
+ if (irModelProto.hasIncidentReport()) {
+ PowerStatsServiceModelProto pssModelProto = irModelProto.getIncidentReport();
+ printEnergyConsumerId(pssModelProto);
+ printEnergyConsumerResults(pssModelProto);
+ } else {
+ System.out.println("Model incident report not found. Exiting.");
}
} catch (IOException e) {
System.out.println("Unable to open incident report file: " + pathToIncidentReport);
diff --git a/wifi/api/current.txt b/wifi/api/current.txt
index f0cf75c..0bc1ff2 100644
--- a/wifi/api/current.txt
+++ b/wifi/api/current.txt
@@ -479,7 +479,9 @@
method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setSsidPattern(@NonNull android.os.PatternMatcher);
method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa2EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa2Passphrase(@NonNull String);
- method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa3EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
+ method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa3Enterprise192BitModeConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
+ method @Deprecated @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa3EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
+ method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa3EnterpriseStandardModeConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
method @NonNull public android.net.wifi.WifiNetworkSpecifier.Builder setWpa3Passphrase(@NonNull String);
}
@@ -527,7 +529,9 @@
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWapiPassphrase(@NonNull String);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa2EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa2Passphrase(@NonNull String);
- method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa3EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa3Enterprise192BitModeConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
+ method @Deprecated @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa3EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa3EnterpriseStandardModeConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa3Passphrase(@NonNull String);
}
diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt
index 754a8dc..da5888b 100644
--- a/wifi/api/system-current.txt
+++ b/wifi/api/system-current.txt
@@ -263,7 +263,7 @@
field public static final int BAND_2GHZ = 1; // 0x1
field public static final int BAND_5GHZ = 2; // 0x2
field public static final int BAND_6GHZ = 4; // 0x4
- field public static final int BAND_ANY = 7; // 0x7
+ field @Deprecated public static final int BAND_ANY = 7; // 0x7
field public static final int RANDOMIZATION_NONE = 0; // 0x0
field public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1
}
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 237922a..2649b66c 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -90,6 +90,9 @@
* Device is allowed to choose the optimal band (2Ghz, 5Ghz, 6Ghz) based on device capability,
* operating country code and current radio conditions.
* @hide
+ *
+ * @deprecated The bands are a bit mask - use any combination of {@code BAND_},
+ * for instance {@code BAND_2GHZ | BAND_5GHZ | BAND_6GHZ}.
*/
@SystemApi
public static final int BAND_ANY = BAND_2GHZ | BAND_5GHZ | BAND_6GHZ;
diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
index e12bb91..35853c0 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
@@ -56,6 +56,16 @@
MacAddress.BROADCAST_ADDRESS;
/**
+ * Set WPA Enterprise type according to certificate security level.
+ * This is for backward compatibility in R.
+ */
+ private static final int WPA3_ENTERPRISE_AUTO = 0;
+ /** Set WPA Enterprise type to standard mode only. */
+ private static final int WPA3_ENTERPRISE_STANDARD = 1;
+ /** Set WPA Enterprise type to 192 bit mode only. */
+ private static final int WPA3_ENTERPRISE_192_BIT = 2;
+
+ /**
* SSID pattern match specified by the app.
*/
private @Nullable PatternMatcher mSsidPatternMatcher;
@@ -87,6 +97,10 @@
*/
private @Nullable WifiEnterpriseConfig mWpa3EnterpriseConfig;
/**
+ * Indicate what type this WPA3-Enterprise network is.
+ */
+ private int mWpa3EnterpriseType = WPA3_ENTERPRISE_AUTO;
+ /**
* This is a network that does not broadcast its SSID, so an
* SSID-specific probe request must be used for scans.
*/
@@ -249,9 +263,14 @@
* sha384WithRSAEncryption (OID 1.2.840.113549.1.1.12) or ecdsa-with-SHA384
* (OID 1.2.840.10045.4.3.3).
*
+ * @deprecated use {@link #setWpa3EnterpriseStandardModeConfig(WifiEnterpriseConfig)} or
+ * {@link #setWpa3Enterprise192BitModeConfig(WifiEnterpriseConfig)} to specify
+ * WPA3-Enterprise type explicitly.
+ *
* @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
* @return Instance of {@link Builder} to enable chaining of the builder method.
*/
+ @Deprecated
public @NonNull Builder setWpa3EnterpriseConfig(
@NonNull WifiEnterpriseConfig enterpriseConfig) {
checkNotNull(enterpriseConfig);
@@ -260,6 +279,58 @@
}
/**
+ * Set the associated enterprise configuration for this network. Needed for authenticating
+ * to standard WPA3-Enterprise networks. See {@link WifiEnterpriseConfig} for description.
+ * For WPA3-Enterprise in 192-bit security mode networks,
+ * see {@link #setWpa3Enterprise192BitModeConfig(WifiEnterpriseConfig)} for description.
+ *
+ * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ */
+ public @NonNull Builder setWpa3EnterpriseStandardModeConfig(
+ @NonNull WifiEnterpriseConfig enterpriseConfig) {
+ checkNotNull(enterpriseConfig);
+ mWpa3EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
+ mWpa3EnterpriseType = WPA3_ENTERPRISE_STANDARD;
+ return this;
+ }
+
+ /**
+ * Set the associated enterprise configuration for this network. Needed for authenticating
+ * to WPA3-Enterprise in 192-bit security mode networks. See {@link WifiEnterpriseConfig}
+ * for description. Both the client and CA certificates must be provided,
+ * and must be of type of either sha384WithRSAEncryption with key length of 3072bit or
+ * more (OID 1.2.840.113549.1.1.12), or ecdsa-with-SHA384 with key length of 384bit or
+ * more (OID 1.2.840.10045.4.3.3).
+ *
+ * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ * @throws IllegalArgumentException if the EAP type or certificates do not
+ * meet 192-bit mode requirements.
+ */
+ public @NonNull Builder setWpa3Enterprise192BitModeConfig(
+ @NonNull WifiEnterpriseConfig enterpriseConfig) {
+ checkNotNull(enterpriseConfig);
+ if (enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.TLS) {
+ throw new IllegalArgumentException("The 192-bit mode network type must be TLS");
+ }
+ if (!WifiEnterpriseConfig.isSuiteBCipherCert(
+ enterpriseConfig.getClientCertificate())) {
+ throw new IllegalArgumentException(
+ "The client certificate does not meet 192-bit mode requirements.");
+ }
+ if (!WifiEnterpriseConfig.isSuiteBCipherCert(
+ enterpriseConfig.getCaCertificate())) {
+ throw new IllegalArgumentException(
+ "The CA certificate does not meet 192-bit mode requirements.");
+ }
+
+ mWpa3EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
+ mWpa3EnterpriseType = WPA3_ENTERPRISE_192_BIT;
+ return this;
+ }
+
+ /**
* Specifies whether this represents a hidden network.
* <p>
* <li>Setting this disallows the usage of {@link #setSsidPattern(PatternMatcher)} since
@@ -289,12 +360,16 @@
configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
configuration.enterpriseConfig = mWpa2EnterpriseConfig;
} else if (mWpa3EnterpriseConfig != null) { // WPA3-Enterprise
- if (mWpa3EnterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS
+ if (mWpa3EnterpriseType == WPA3_ENTERPRISE_AUTO
+ && mWpa3EnterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS
&& WifiEnterpriseConfig.isSuiteBCipherCert(
mWpa3EnterpriseConfig.getClientCertificate())
&& WifiEnterpriseConfig.isSuiteBCipherCert(
mWpa3EnterpriseConfig.getCaCertificate())) {
- // WPA3-Enterprise in 192-bit security mode (Suite-B)
+ // WPA3-Enterprise in 192-bit security mode
+ configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
+ } else if (mWpa3EnterpriseType == WPA3_ENTERPRISE_192_BIT) {
+ // WPA3-Enterprise in 192-bit security mode
configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
} else {
// WPA3-Enterprise
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index c1f9005..0b36870 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -55,6 +55,16 @@
private static final int UNASSIGNED_PRIORITY = -1;
/**
+ * Set WPA Enterprise type according to certificate security level.
+ * This is for backward compatibility in R.
+ */
+ private static final int WPA3_ENTERPRISE_AUTO = 0;
+ /** Set WPA Enterprise type to standard mode only. */
+ private static final int WPA3_ENTERPRISE_STANDARD = 1;
+ /** Set WPA Enterprise type to 192 bit mode only. */
+ private static final int WPA3_ENTERPRISE_192_BIT = 2;
+
+ /**
* SSID of the network.
*/
private String mSsid;
@@ -85,6 +95,10 @@
*/
private @Nullable WifiEnterpriseConfig mWpa3EnterpriseConfig;
/**
+ * Indicate what type this WPA3-Enterprise network is.
+ */
+ private int mWpa3EnterpriseType = WPA3_ENTERPRISE_AUTO;
+ /**
* The passpoint config for use with Hotspot 2.0 network
*/
private @Nullable PasspointConfiguration mPasspointConfiguration;
@@ -311,11 +325,16 @@
* sha384WithRSAEncryption (OID 1.2.840.113549.1.1.12) or ecdsa-with-SHA384
* (OID 1.2.840.10045.4.3.3).
*
+ * @deprecated use {@link #setWpa3EnterpriseStandardModeConfig(WifiEnterpriseConfig)} or
+ * {@link #setWpa3Enterprise192BitModeConfig(WifiEnterpriseConfig)} to specify
+ * WPA3-Enterprise type explicitly.
+ *
* @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
* @return Instance of {@link Builder} to enable chaining of the builder method.
* @throws IllegalArgumentException if configuration CA certificate or
* AltSubjectMatch/DomainSuffixMatch is not set.
*/
+ @Deprecated
public @NonNull Builder setWpa3EnterpriseConfig(
@NonNull WifiEnterpriseConfig enterpriseConfig) {
checkNotNull(enterpriseConfig);
@@ -327,6 +346,63 @@
}
/**
+ * Set the associated enterprise configuration for this network. Needed for authenticating
+ * to WPA3-Enterprise standard networks. See {@link WifiEnterpriseConfig} for description.
+ * For WPA3-Enterprise in 192-bit security mode networks,
+ * see {@link #setWpa3Enterprise192BitModeConfig(WifiEnterpriseConfig)} for description.
+ *
+ * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ * @throws IllegalArgumentException if configuration CA certificate or
+ * AltSubjectMatch/DomainSuffixMatch is not set.
+ */
+ public @NonNull Builder setWpa3EnterpriseStandardModeConfig(
+ @NonNull WifiEnterpriseConfig enterpriseConfig) {
+ checkNotNull(enterpriseConfig);
+ if (enterpriseConfig.isInsecure()) {
+ throw new IllegalArgumentException("Enterprise configuration is insecure");
+ }
+ mWpa3EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
+ mWpa3EnterpriseType = WPA3_ENTERPRISE_STANDARD;
+ return this;
+ }
+
+ /**
+ * Set the associated enterprise configuration for this network. Needed for authenticating
+ * to WPA3-Enterprise in 192-bit security mode networks. See {@link WifiEnterpriseConfig}
+ * for description. Both the client and CA certificates must be provided,
+ * and must be of type of either sha384WithRSAEncryption with key length of 3072bit or
+ * more (OID 1.2.840.113549.1.1.12), or ecdsa-with-SHA384 with key length of 384bit or
+ * more (OID 1.2.840.10045.4.3.3).
+ *
+ * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ * @throws IllegalArgumentException if the EAP type or certificates do not
+ * meet 192-bit mode requirements.
+ */
+ public @NonNull Builder setWpa3Enterprise192BitModeConfig(
+ @NonNull WifiEnterpriseConfig enterpriseConfig) {
+ checkNotNull(enterpriseConfig);
+ if (enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.TLS) {
+ throw new IllegalArgumentException("The 192-bit mode network type must be TLS");
+ }
+ if (!WifiEnterpriseConfig.isSuiteBCipherCert(
+ enterpriseConfig.getClientCertificate())) {
+ throw new IllegalArgumentException(
+ "The client certificate does not meet 192-bit mode requirements.");
+ }
+ if (!WifiEnterpriseConfig.isSuiteBCipherCert(
+ enterpriseConfig.getCaCertificate())) {
+ throw new IllegalArgumentException(
+ "The CA certificate does not meet 192-bit mode requirements.");
+ }
+
+ mWpa3EnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
+ mWpa3EnterpriseType = WPA3_ENTERPRISE_192_BIT;
+ return this;
+ }
+
+ /**
* Set the associated Passpoint configuration for this network. Needed for authenticating
* to Hotspot 2.0 networks. See {@link PasspointConfiguration} for description.
*
@@ -652,12 +728,16 @@
configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
configuration.enterpriseConfig = mWpa2EnterpriseConfig;
} else if (mWpa3EnterpriseConfig != null) { // WPA3-Enterprise
- if (mWpa3EnterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS
+ if (mWpa3EnterpriseType == WPA3_ENTERPRISE_AUTO
+ && mWpa3EnterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS
&& WifiEnterpriseConfig.isSuiteBCipherCert(
mWpa3EnterpriseConfig.getClientCertificate())
&& WifiEnterpriseConfig.isSuiteBCipherCert(
mWpa3EnterpriseConfig.getCaCertificate())) {
- // WPA3-Enterprise in 192-bit security mode (Suite-B)
+ // WPA3-Enterprise in 192-bit security mode
+ configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
+ } else if (mWpa3EnterpriseType == WPA3_ENTERPRISE_192_BIT) {
+ // WPA3-Enterprise in 192-bit security mode
configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
} else {
// WPA3-Enterprise
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
index 6f47f3d..464f462 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
@@ -141,11 +141,11 @@
}
/**
- * Validate correctness of WifiNetworkSuggestion object created by
- * {@link WifiNetworkSuggestion.Builder#build()} for WPA3-Enterprise network.
+ * Validate correctness of WifiNetworkSpecifier object created by
+ * {@link WifiNetworkSpecifier.Builder#build()} for WPA3-Enterprise network.
*/
@Test
- public void testWifiNetworkSuggestionBuilderForWpa3EapNetwork() {
+ public void testWifiNetworkSpecifierBuilderForWpa3EapNetwork() {
WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
enterpriseConfig.setCaCertificate(FakeKeys.CA_CERT0);
@@ -174,11 +174,118 @@
}
/**
- * Validate correctness of WifiNetworkSuggestion object created by
- * {@link WifiNetworkSuggestion.Builder#build()} for WPA3-Enterprise 192-bit RSA SuiteB network.
+ * Validate correctness of WifiNetworkSpecifier object created by
+ * {@link WifiNetworkSpecifier.Builder#build()} for WPA3-Enterprise network.
*/
@Test
- public void testWifiNetworkSuggestionBuilderForWpa3SuiteBRsaEapNetwork() {
+ public void testWifiNetworkSpecifierBuilderForWpa3EapNetworkWithStandardApi() {
+ WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+ enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ enterpriseConfig.setCaCertificate(FakeKeys.CA_CERT0);
+ enterpriseConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH);
+
+ NetworkSpecifier specifier = new WifiNetworkSpecifier.Builder()
+ .setSsid(TEST_SSID)
+ .setWpa3EnterpriseStandardModeConfig(enterpriseConfig)
+ .build();
+
+ assertTrue(specifier instanceof WifiNetworkSpecifier);
+ WifiNetworkSpecifier wifiNetworkSpecifier = (WifiNetworkSpecifier) specifier;
+
+ assertEquals("\"" + TEST_SSID + "\"", wifiNetworkSpecifier.wifiConfiguration.SSID);
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.IEEE8021X));
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.WPA_EAP));
+ assertFalse(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+ .get(WifiConfiguration.GroupCipher.CCMP));
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.requirePmf);
+ assertNull(wifiNetworkSpecifier.wifiConfiguration.preSharedKey);
+ assertNotNull(wifiNetworkSpecifier.wifiConfiguration.enterpriseConfig);
+ }
+
+ /**
+ * Validate correctness of WifiNetworkSpecifier object created by
+ * {@link WifiNetworkSpecifier.Builder#build()} for WPA3-Enterprise network
+ * with 192-bit RSA certificates.
+ */
+ @Test
+ public void testWifiNetworkSpecifierBuilderForWpa3EapNetworkWithSuiteBRsaCerts() {
+ WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+ enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ enterpriseConfig.setCaCertificate(FakeKeys.CA_SUITE_B_RSA3072_CERT);
+ enterpriseConfig.setClientKeyEntryWithCertificateChain(FakeKeys.CLIENT_SUITE_B_RSA3072_KEY,
+ new X509Certificate[] {FakeKeys.CLIENT_SUITE_B_RSA3072_CERT});
+
+ enterpriseConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH);
+
+ NetworkSpecifier specifier = new WifiNetworkSpecifier.Builder()
+ .setSsid(TEST_SSID)
+ .setWpa3EnterpriseStandardModeConfig(enterpriseConfig)
+ .build();
+
+ assertTrue(specifier instanceof WifiNetworkSpecifier);
+ WifiNetworkSpecifier wifiNetworkSpecifier = (WifiNetworkSpecifier) specifier;
+
+ assertEquals("\"" + TEST_SSID + "\"", wifiNetworkSpecifier.wifiConfiguration.SSID);
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.IEEE8021X));
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.WPA_EAP));
+ assertFalse(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+ .get(WifiConfiguration.GroupCipher.CCMP));
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.requirePmf);
+ assertNull(wifiNetworkSpecifier.wifiConfiguration.preSharedKey);
+ assertNotNull(wifiNetworkSpecifier.wifiConfiguration.enterpriseConfig);
+ }
+
+ /**
+ * Validate correctness of WifiNetworkSpecifier object created by
+ * {@link WifiNetworkSpecifier.Builder#build()} for WPA3-Enterprise network
+ * with 192-bit ECC certificates.
+ */
+ @Test
+ public void testWifiNetworkSpecifierBuilderForWpa3EapNetworkWithSuiteBEccCerts() {
+ WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+ enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ enterpriseConfig.setCaCertificate(FakeKeys.CA_SUITE_B_ECDSA_CERT);
+ enterpriseConfig.setClientKeyEntryWithCertificateChain(FakeKeys.CLIENT_SUITE_B_ECC_KEY,
+ new X509Certificate[] {FakeKeys.CLIENT_SUITE_B_ECDSA_CERT});
+
+ enterpriseConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH);
+
+ NetworkSpecifier specifier = new WifiNetworkSpecifier.Builder()
+ .setSsid(TEST_SSID)
+ .setWpa3EnterpriseStandardModeConfig(enterpriseConfig)
+ .build();
+
+ assertTrue(specifier instanceof WifiNetworkSpecifier);
+ WifiNetworkSpecifier wifiNetworkSpecifier = (WifiNetworkSpecifier) specifier;
+
+ assertEquals("\"" + TEST_SSID + "\"", wifiNetworkSpecifier.wifiConfiguration.SSID);
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.IEEE8021X));
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.WPA_EAP));
+ assertFalse(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+ .get(WifiConfiguration.GroupCipher.CCMP));
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.requirePmf);
+ assertNull(wifiNetworkSpecifier.wifiConfiguration.preSharedKey);
+ assertNotNull(wifiNetworkSpecifier.wifiConfiguration.enterpriseConfig);
+ }
+
+ /**
+ * Validate correctness of WifiNetworkSpecifier object created by
+ * {@link WifiNetworkSpecifier.Builder#build()} for WPA3-Enterprise 192-bit RSA SuiteB network.
+ */
+ @Test
+ public void testWifiNetworkSpecifierBuilderForWpa3SuiteBRsaEapNetwork() {
WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
enterpriseConfig.setCaCertificate(FakeKeys.CA_SUITE_B_RSA3072_CERT);
@@ -208,11 +315,11 @@
}
/**
- * Validate correctness of WifiNetworkSuggestion object created by
- * {@link WifiNetworkSuggestion.Builder#build()} for WPA3-Enterprise 192-bit ECC SuiteB network.
+ * Validate correctness of WifiNetworkSpecifier object created by
+ * {@link WifiNetworkSpecifier.Builder#build()} for WPA3-Enterprise 192-bit ECC SuiteB network.
*/
@Test
- public void testWifiNetworkSuggestionBuilderForWpa3SuiteBEccEapNetwork() {
+ public void testWifiNetworkSpecifierBuilderForWpa3SuiteBEccEapNetwork() {
WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
enterpriseConfig.setCaCertificate(FakeKeys.CA_SUITE_B_ECDSA_CERT);
@@ -242,6 +349,74 @@
}
/**
+ * Validate correctness of WifiNetworkSpecifier object created by
+ * {@link WifiNetworkSpecifier.Builder#build()} for WPA3-Enterprise 192-bit RSA SuiteB network.
+ */
+ @Test
+ public void testWifiNetworkSpecifierBuilderForWpa3SuiteBRsaEapNetworkWith192BitApi() {
+ WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+ enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ enterpriseConfig.setCaCertificate(FakeKeys.CA_SUITE_B_RSA3072_CERT);
+ enterpriseConfig.setClientKeyEntryWithCertificateChain(FakeKeys.CLIENT_SUITE_B_RSA3072_KEY,
+ new X509Certificate[] {FakeKeys.CLIENT_SUITE_B_RSA3072_CERT});
+
+ enterpriseConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH);
+
+ NetworkSpecifier specifier = new WifiNetworkSpecifier.Builder()
+ .setSsid(TEST_SSID)
+ .setWpa3Enterprise192BitModeConfig(enterpriseConfig)
+ .build();
+
+ assertTrue(specifier instanceof WifiNetworkSpecifier);
+ WifiNetworkSpecifier wifiNetworkSpecifier = (WifiNetworkSpecifier) specifier;
+
+ assertEquals("\"" + TEST_SSID + "\"", wifiNetworkSpecifier.wifiConfiguration.SSID);
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+ .get(WifiConfiguration.GroupCipher.GCMP_256));
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupManagementCiphers
+ .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.requirePmf);
+ assertNull(wifiNetworkSpecifier.wifiConfiguration.preSharedKey);
+ assertNotNull(wifiNetworkSpecifier.wifiConfiguration.enterpriseConfig);
+ }
+
+ /**
+ * Validate correctness of WifiNetworkSpecifier object created by
+ * {@link WifiNetworkSpecifier.Builder#build()} for WPA3-Enterprise 192-bit ECC SuiteB network.
+ */
+ @Test
+ public void testWifiNetworkSpecifierBuilderForWpa3SuiteBEccEapNetworkWith192BitApi() {
+ WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+ enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ enterpriseConfig.setCaCertificate(FakeKeys.CA_SUITE_B_ECDSA_CERT);
+ enterpriseConfig.setClientKeyEntryWithCertificateChain(FakeKeys.CLIENT_SUITE_B_ECC_KEY,
+ new X509Certificate[] {FakeKeys.CLIENT_SUITE_B_ECDSA_CERT});
+
+ enterpriseConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH);
+
+ NetworkSpecifier specifier = new WifiNetworkSpecifier.Builder()
+ .setSsid(TEST_SSID)
+ .setWpa3Enterprise192BitModeConfig(enterpriseConfig)
+ .build();
+
+ assertTrue(specifier instanceof WifiNetworkSpecifier);
+ WifiNetworkSpecifier wifiNetworkSpecifier = (WifiNetworkSpecifier) specifier;
+
+ assertEquals("\"" + TEST_SSID + "\"", wifiNetworkSpecifier.wifiConfiguration.SSID);
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+ .get(WifiConfiguration.GroupCipher.GCMP_256));
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupManagementCiphers
+ .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
+ assertTrue(wifiNetworkSpecifier.wifiConfiguration.requirePmf);
+ assertNull(wifiNetworkSpecifier.wifiConfiguration.preSharedKey);
+ assertNotNull(wifiNetworkSpecifier.wifiConfiguration.enterpriseConfig);
+ }
+
+ /**
* Ensure {@link WifiNetworkSpecifier.Builder#setSsid(String)} throws an exception
* when the string is not Unicode.
*/
@@ -430,15 +605,16 @@
/**
* Ensure {@link WifiNetworkSpecifier.Builder#build()} throws an exception
* when both {@link WifiNetworkSpecifier.Builder#setWpa3Passphrase(String)} and
- * {@link WifiNetworkSpecifier.Builder#setWpa3EnterpriseConfig(WifiEnterpriseConfig)} are
- * invoked.
+ * {@link WifiNetworkSpecifier.Builder
+ * #setWpa3EnterpriseStandardModeConfig(WifiEnterpriseConfig)}
+ * are invoked.
*/
@Test(expected = IllegalStateException.class)
public void testWifiNetworkSpecifierBuilderWithBothWpa3PasphraseAndEnterprise() {
new WifiNetworkSpecifier.Builder()
.setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
.setWpa3Passphrase(TEST_PRESHARED_KEY)
- .setWpa3EnterpriseConfig(new WifiEnterpriseConfig())
+ .setWpa3EnterpriseStandardModeConfig(new WifiEnterpriseConfig())
.build();
}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index 3f9ce5b..bb84120 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -280,6 +280,116 @@
/**
* Validate correctness of WifiNetworkSuggestion object created by
+ * {@link WifiNetworkSuggestion.Builder#build()} for WPA3-Enterprise standard network.
+ */
+ @Test
+ public void testWifiNetworkSuggestionBuilderForWpa3EapNetworkWithStandardApi() {
+ WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+ enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ enterpriseConfig.setCaCertificate(FakeKeys.CA_CERT0);
+ enterpriseConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH);
+
+ WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
+ .setSsid(TEST_SSID)
+ .setWpa3EnterpriseStandardModeConfig(enterpriseConfig)
+ .build();
+
+ assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+ assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.IEEE8021X));
+ assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.WPA_EAP));
+ assertFalse(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+ assertTrue(suggestion.wifiConfiguration.allowedGroupCiphers
+ .get(WifiConfiguration.GroupCipher.CCMP));
+ assertTrue(suggestion.wifiConfiguration.requirePmf);
+ assertNull(suggestion.wifiConfiguration.preSharedKey);
+ // allowedSuiteBCiphers are set according to the loaded certificate and cannot be tested
+ // here.
+ assertTrue(suggestion.isUserAllowedToManuallyConnect);
+ assertTrue(suggestion.isInitialAutoJoinEnabled);
+ assertNotNull(suggestion.getEnterpriseConfig());
+ }
+
+ /**
+ * Validate correctness of WifiNetworkSuggestion object created by
+ * {@link WifiNetworkSuggestion.Builder#build()} for WPA3-Enterprise network
+ * with 192-bit RSA SuiteB certificates.
+ */
+ @Test
+ public void testWifiNetworkSuggestionBuilderForWpa3EapNetworkWithSuiteBRsaCerts() {
+ WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+ enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ enterpriseConfig.setCaCertificate(FakeKeys.CA_SUITE_B_RSA3072_CERT);
+ enterpriseConfig.setClientKeyEntryWithCertificateChain(FakeKeys.CLIENT_SUITE_B_RSA3072_KEY,
+ new X509Certificate[] {FakeKeys.CLIENT_SUITE_B_RSA3072_CERT});
+
+ enterpriseConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH);
+
+ WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
+ .setSsid(TEST_SSID)
+ .setWpa3EnterpriseStandardModeConfig(enterpriseConfig)
+ .build();
+
+ assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+ assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.IEEE8021X));
+ assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.WPA_EAP));
+ assertFalse(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+ assertTrue(suggestion.wifiConfiguration.allowedGroupCiphers
+ .get(WifiConfiguration.GroupCipher.CCMP));
+ assertTrue(suggestion.wifiConfiguration.requirePmf);
+ assertNull(suggestion.wifiConfiguration.preSharedKey);
+ // allowedSuiteBCiphers are set according to the loaded certificate and cannot be tested
+ // here.
+ assertTrue(suggestion.isUserAllowedToManuallyConnect);
+ assertTrue(suggestion.isInitialAutoJoinEnabled);
+ assertNotNull(suggestion.getEnterpriseConfig());
+ }
+
+ /**
+ * Validate correctness of WifiNetworkSuggestion object created by
+ * {@link WifiNetworkSuggestion.Builder#build()} for WPA3-Enterprise network
+ * with 192-bit ECC certificates.
+ */
+ @Test
+ public void testWifiNetworkSuggestionBuilderForWpa3EapNetworkWithSuiteBEccCerts() {
+ WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+ enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ enterpriseConfig.setCaCertificate(FakeKeys.CA_SUITE_B_ECDSA_CERT);
+ enterpriseConfig.setClientKeyEntryWithCertificateChain(FakeKeys.CLIENT_SUITE_B_ECC_KEY,
+ new X509Certificate[] {FakeKeys.CLIENT_SUITE_B_ECDSA_CERT});
+
+ enterpriseConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH);
+
+ WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
+ .setSsid(TEST_SSID)
+ .setWpa3EnterpriseStandardModeConfig(enterpriseConfig)
+ .build();
+
+ assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+ assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.IEEE8021X));
+ assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.WPA_EAP));
+ assertFalse(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+ assertTrue(suggestion.wifiConfiguration.allowedGroupCiphers
+ .get(WifiConfiguration.GroupCipher.CCMP));
+ assertTrue(suggestion.wifiConfiguration.requirePmf);
+ assertNull(suggestion.wifiConfiguration.preSharedKey);
+ // allowedSuiteBCiphers are set according to the loaded certificate and cannot be tested
+ // here.
+ assertTrue(suggestion.isUserAllowedToManuallyConnect);
+ assertTrue(suggestion.isInitialAutoJoinEnabled);
+ assertNotNull(suggestion.getEnterpriseConfig());
+ }
+
+ /**
+ * Validate correctness of WifiNetworkSuggestion object created by
* {@link WifiNetworkSuggestion.Builder#build()} for WPA3-Enterprise 192-bit RSA SuiteB network.
*/
@Test
@@ -315,6 +425,41 @@
/**
* Validate correctness of WifiNetworkSuggestion object created by
+ * {@link WifiNetworkSuggestion.Builder#build()} for WPA3-Enterprise 192-bit RSA SuiteB network.
+ */
+ @Test
+ public void testWifiNetworkSuggestionBuilderForWpa3SuiteBRsaEapNetworWith192BitApi() {
+ WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+ enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ enterpriseConfig.setCaCertificate(FakeKeys.CA_SUITE_B_RSA3072_CERT);
+ enterpriseConfig.setClientKeyEntryWithCertificateChain(FakeKeys.CLIENT_SUITE_B_RSA3072_KEY,
+ new X509Certificate[] {FakeKeys.CLIENT_SUITE_B_RSA3072_CERT});
+
+ enterpriseConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH);
+
+ WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
+ .setSsid(TEST_SSID)
+ .setWpa3Enterprise192BitModeConfig(enterpriseConfig)
+ .build();
+
+ assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+ assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+ assertTrue(suggestion.wifiConfiguration.allowedGroupCiphers
+ .get(WifiConfiguration.GroupCipher.GCMP_256));
+ assertTrue(suggestion.wifiConfiguration.allowedGroupManagementCiphers
+ .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
+ assertTrue(suggestion.wifiConfiguration.requirePmf);
+ assertNull(suggestion.wifiConfiguration.preSharedKey);
+ // allowedSuiteBCiphers are set according to the loaded certificate and cannot be tested
+ // here.
+ assertTrue(suggestion.isUserAllowedToManuallyConnect);
+ assertTrue(suggestion.isInitialAutoJoinEnabled);
+ assertNotNull(suggestion.getEnterpriseConfig());
+ }
+
+ /**
+ * Validate correctness of WifiNetworkSuggestion object created by
* {@link WifiNetworkSuggestion.Builder#build()} for WPA3-Enterprise 192-bit ECC SuiteB network.
*/
@Test
@@ -349,6 +494,41 @@
}
/**
+ * Validate correctness of WifiNetworkSuggestion object created by
+ * {@link WifiNetworkSuggestion.Builder#build()} for WPA3-Enterprise 192-bit ECC SuiteB network.
+ */
+ @Test
+ public void testWifiNetworkSuggestionBuilderForWpa3SuiteBEccEapNetworkWith192BitApi() {
+ WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+ enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ enterpriseConfig.setCaCertificate(FakeKeys.CA_SUITE_B_ECDSA_CERT);
+ enterpriseConfig.setClientKeyEntryWithCertificateChain(FakeKeys.CLIENT_SUITE_B_ECC_KEY,
+ new X509Certificate[] {FakeKeys.CLIENT_SUITE_B_ECDSA_CERT});
+
+ enterpriseConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH);
+
+ WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
+ .setSsid(TEST_SSID)
+ .setWpa3Enterprise192BitModeConfig(enterpriseConfig)
+ .build();
+
+ assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+ assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+ .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+ assertTrue(suggestion.wifiConfiguration.allowedGroupCiphers
+ .get(WifiConfiguration.GroupCipher.GCMP_256));
+ assertTrue(suggestion.wifiConfiguration.allowedGroupManagementCiphers
+ .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
+ assertTrue(suggestion.wifiConfiguration.requirePmf);
+ assertNull(suggestion.wifiConfiguration.preSharedKey);
+ // allowedSuiteBCiphers are set according to the loaded certificate and cannot be tested
+ // here.
+ assertTrue(suggestion.isUserAllowedToManuallyConnect);
+ assertTrue(suggestion.isInitialAutoJoinEnabled);
+ assertNotNull(suggestion.getEnterpriseConfig());
+ }
+
+ /**
* Ensure create enterprise suggestion requires CA, when CA certificate is missing, will throw
* an exception.
*/
@@ -378,7 +558,7 @@
WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
.setSsid(TEST_SSID)
- .setWpa3EnterpriseConfig(enterpriseConfig)
+ .setWpa3EnterpriseStandardModeConfig(enterpriseConfig)
.build();
}
@@ -600,15 +780,16 @@
/**
* Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception
* when both {@link WifiNetworkSuggestion.Builder#setWpa3Passphrase(String)} and
- * {@link WifiNetworkSuggestion.Builder#setWpa3EnterpriseConfig(WifiEnterpriseConfig)} are
- * invoked.
+ * {@link WifiNetworkSuggestion.Builderi
+ * #setWpa3EnterpriseStandardModeConfig(WifiEnterpriseConfig)}
+ * are invoked.
*/
@Test(expected = IllegalStateException.class)
public void testWifiNetworkSuggestionBuilderWithBothWpa3PasphraseAndEnterprise() {
new WifiNetworkSuggestion.Builder()
.setSsid(TEST_SSID)
.setWpa3Passphrase(TEST_PRESHARED_KEY)
- .setWpa3EnterpriseConfig(new WifiEnterpriseConfig())
+ .setWpa3EnterpriseStandardModeConfig(new WifiEnterpriseConfig())
.build();
}
@@ -670,7 +851,9 @@
/**
* Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception
- * when both {@link WifiNetworkSuggestion.Builder#setWpa3EnterpriseConfig(WifiEnterpriseConfig)}
+ * when both
+ * {@link WifiNetworkSuggestion.Builder
+ * #setWpa3EnterpriseStandardModeConfig(WifiEnterpriseConfig)}
* and {@link WifiNetworkSuggestion.Builder#setPasspointConfig(PasspointConfiguration)} are
* invoked.
*/
@@ -678,7 +861,7 @@
public void testWifiNetworkSuggestionBuilderWithBothEnterpriseAndPasspointConfig() {
PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig();
new WifiNetworkSuggestion.Builder()
- .setWpa3EnterpriseConfig(new WifiEnterpriseConfig())
+ .setWpa3EnterpriseStandardModeConfig(new WifiEnterpriseConfig())
.setPasspointConfig(passpointConfiguration)
.build();
}