Merge "[Jetpack] Read display_featuers from core res"
diff --git a/Android.bp b/Android.bp
index eacf57c..c6f9362 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1029,6 +1029,7 @@
name: "incremental_manager_aidl",
srcs: [
"core/java/android/os/incremental/IIncrementalService.aidl",
+ "core/java/android/os/incremental/IStorageLoadingProgressListener.aidl",
"core/java/android/os/incremental/IncrementalNewFileParams.aidl",
"core/java/android/os/incremental/IStorageHealthListener.aidl",
"core/java/android/os/incremental/StorageHealthCheckParams.aidl",
diff --git a/apct-tests/perftests/core/src/android/os/ParcelObtainPerfTest.java b/apct-tests/perftests/core/src/android/os/ParcelObtainPerfTest.java
new file mode 100644
index 0000000..760ae12
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/ParcelObtainPerfTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.os;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ParcelObtainPerfTest {
+ private static final int ITERATIONS = 1_000_000;
+
+ @Test
+ public void timeContention_01() throws Exception {
+ timeContention(1);
+ }
+
+ @Test
+ public void timeContention_04() throws Exception {
+ timeContention(4);
+ }
+
+ @Test
+ public void timeContention_16() throws Exception {
+ timeContention(16);
+ }
+
+ private static void timeContention(int numThreads) throws Exception {
+ final long start = SystemClock.elapsedRealtime();
+ {
+ final ObtainThread[] threads = new ObtainThread[numThreads];
+ for (int i = 0; i < numThreads; i++) {
+ final ObtainThread thread = new ObtainThread(ITERATIONS / numThreads);
+ thread.start();
+ threads[i] = thread;
+ }
+ for (int i = 0; i < numThreads; i++) {
+ threads[i].join();
+ }
+ }
+ final long duration = SystemClock.elapsedRealtime() - start;
+
+ final Bundle results = new Bundle();
+ results.putLong("duration", duration);
+ InstrumentationRegistry.getInstrumentation().sendStatus(0, results);
+ }
+
+ public static class ObtainThread extends Thread {
+ public int iterations;
+
+ public ObtainThread(int iterations) {
+ this.iterations = iterations;
+ }
+
+ @Override
+ public void run() {
+ while (iterations-- > 0) {
+ final Parcel data = Parcel.obtain();
+ final Parcel reply = Parcel.obtain();
+ try {
+ data.writeInt(32);
+ reply.writeInt(32);
+ } finally {
+ reply.recycle();
+ data.recycle();
+ }
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java b/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
index 4db9262..be2f9d7 100644
--- a/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
@@ -159,21 +159,6 @@
}
@Test
- public void timeObtainRecycle() {
- // Use up the pooled instances.
- // A lot bigger than the actual size but in case someone increased it.
- final int POOL_SIZE = 100;
- for (int i = 0; i < POOL_SIZE; i++) {
- Parcel.obtain();
- }
-
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
- Parcel.obtain().recycle();
- }
- }
-
- @Test
public void timeWriteException() {
timeWriteException(false);
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
index 42725c5..bf5781b 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
@@ -101,10 +101,15 @@
* version {@link android.os.Build.VERSION_CODES#Q}. As such, the system may throttle calls to
* this API if calls are made too frequently in a short amount of time.
*
+ * <p>Note: The JobService component needs to be enabled in order to successfully schedule a
+ * job.
+ *
* @param job The job you wish scheduled. See
* {@link android.app.job.JobInfo.Builder JobInfo.Builder} for more detail on the sorts of jobs
* you can schedule.
* @return the result of the schedule request.
+ * @throws IllegalArgumentException if the specified {@link JobService} doesn't exist or is
+ * disabled.
*/
public abstract @Result int schedule(@NonNull JobInfo job);
@@ -137,11 +142,21 @@
* work you are enqueue, since currently this will always be treated as a different JobInfo,
* even if the ClipData contents are exactly the same.</p>
*
+ * <p class="caution"><strong>Note:</strong> Scheduling a job can have a high cost, even if it's
+ * rescheduling the same job and the job didn't execute, especially on platform versions before
+ * version {@link android.os.Build.VERSION_CODES#Q}. As such, the system may throttle calls to
+ * this API if calls are made too frequently in a short amount of time.
+ *
+ * <p>Note: The JobService component needs to be enabled in order to successfully schedule a
+ * job.
+ *
* @param job The job you wish to enqueue work for. See
* {@link android.app.job.JobInfo.Builder JobInfo.Builder} for more detail on the sorts of jobs
* you can schedule.
* @param work New work to enqueue. This will be available later when the job starts running.
* @return the result of the enqueue request.
+ * @throws IllegalArgumentException if the specified {@link JobService} doesn't exist or is
+ * disabled.
*/
public abstract @Result int enqueue(@NonNull JobInfo job, @NonNull JobWorkItem work);
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index d8386b5..59915e1 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -2178,11 +2178,10 @@
if (getContext().getResources().getBoolean(
com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {
- mLocationRequest = LocationRequest.create()
+ mLocationRequest = new LocationRequest.Builder(/*intervalMillis=*/ 0)
.setQuality(LocationRequest.ACCURACY_FINE)
- .setInterval(0)
- .setFastestInterval(0)
- .setNumUpdates(1);
+ .setMaxUpdates(1)
+ .build();
}
mConstraintController = mInjector.getConstraintController(
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index ec7e99e..a1be7cb 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -2722,6 +2722,14 @@
mPendingBackgroundAlarms.removeAt(i);
}
}
+ for (int i = mPendingNonWakeupAlarms.size() - 1; i >= 0; i--) {
+ final Alarm a = mPendingNonWakeupAlarms.get(i);
+ if (a.matches(operation, directReceiver)) {
+ // Don't set didRemove, since this doesn't impact the scheduled alarms.
+ mPendingNonWakeupAlarms.remove(i);
+ decrementAlarmCount(a.uid, 1);
+ }
+ }
if (didRemove) {
if (DEBUG_BATCH) {
Slog.v(TAG, "remove(operation) changed bounds; rebatching");
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 cf4caea..b638fef 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -87,11 +87,11 @@
import com.android.server.DeviceIdleInternal;
import com.android.server.JobSchedulerBackgroundThread;
import com.android.server.LocalServices;
-import com.android.server.SystemService.TargetUser;
import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
import com.android.server.job.controllers.BackgroundJobsController;
import com.android.server.job.controllers.BatteryController;
+import com.android.server.job.controllers.ComponentController;
import com.android.server.job.controllers.ConnectivityController;
import com.android.server.job.controllers.ContentObserverController;
import com.android.server.job.controllers.DeviceIdleJobsController;
@@ -1484,6 +1484,7 @@
mControllers.add(mDeviceIdleJobsController);
mQuotaController = new QuotaController(this);
mControllers.add(mQuotaController);
+ mControllers.add(new ComponentController(this));
mRestrictiveControllers = new ArrayList<>();
mRestrictiveControllers.add(mBatteryController);
@@ -2300,21 +2301,12 @@
return false;
}
- // The expensive check: validate that the defined package+service is
- // still present & viable.
+ // Validate that the defined package+service is still present & viable.
return isComponentUsable(job);
}
private boolean isComponentUsable(@NonNull JobStatus job) {
- final ServiceInfo service;
- try {
- // TODO: cache result until we're notified that something in the package changed.
- service = AppGlobals.getPackageManager().getServiceInfo(
- job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
- job.getUserId());
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
+ final ServiceInfo service = job.serviceInfo;
if (service == null) {
if (DEBUG) {
@@ -3104,7 +3096,7 @@
try {
componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
js.getServiceComponent(),
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+ PackageManager.MATCH_DIRECT_BOOT_AUTO,
js.getUserId()) != null);
} catch (RemoteException e) {
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
new file mode 100644
index 0000000..b47a210
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.job.controllers;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppGlobals;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArrayMap;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.job.JobSchedulerService;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+/**
+ * Controller that tracks changes in the service component's enabled state.
+ */
+public class ComponentController extends StateController {
+ private static final String TAG = "JobScheduler.Component";
+ private static final boolean DEBUG = JobSchedulerService.DEBUG
+ || Log.isLoggable(TAG, Log.DEBUG);
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action == null) {
+ Slog.wtf(TAG, "Intent action was null");
+ return;
+ }
+ switch (action) {
+ case Intent.ACTION_PACKAGE_CHANGED:
+ final Uri uri = intent.getData();
+ final String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
+ final String[] changedComponents = intent.getStringArrayExtra(
+ Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+ if (pkg != null && changedComponents != null && changedComponents.length > 0) {
+ updateComponentStateForPackage(pkg);
+ }
+ break;
+ case Intent.ACTION_USER_UNLOCKED:
+ case Intent.ACTION_USER_STOPPED:
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ updateComponentStateForUser(userId);
+ break;
+ }
+ }
+ };
+
+ private final ComponentStateUpdateFunctor mComponentStateUpdateFunctor =
+ new ComponentStateUpdateFunctor();
+
+ public ComponentController(JobSchedulerService service) {
+ super(service);
+
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addDataScheme("package");
+ mContext.registerReceiverAsUser(
+ mBroadcastReceiver, UserHandle.ALL, filter, null, null);
+ final IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
+ userFilter.addAction(Intent.ACTION_USER_STOPPED);
+ mContext.registerReceiverAsUser(
+ mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
+
+ }
+
+ @Override
+ public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
+ updateComponentEnabledStateLocked(jobStatus, null);
+ }
+
+ @Override
+ public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
+ boolean forUpdate) {
+
+ }
+
+ @Nullable
+ private ServiceInfo getServiceInfo(JobStatus jobStatus,
+ @Nullable SparseArrayMap<ComponentName, ServiceInfo> cache) {
+ final ComponentName cn = jobStatus.getServiceComponent();
+ ServiceInfo si = null;
+ if (cache != null) {
+ si = cache.get(jobStatus.getUserId(), cn);
+ }
+ if (si == null) {
+ try {
+ si = AppGlobals.getPackageManager().getServiceInfo(
+ cn, PackageManager.MATCH_DIRECT_BOOT_AUTO, jobStatus.getUserId());
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ if (cache != null) {
+ cache.add(jobStatus.getUserId(), cn, si);
+ }
+ }
+ return si;
+ }
+
+ private boolean updateComponentEnabledStateLocked(JobStatus jobStatus,
+ @Nullable SparseArrayMap<ComponentName, ServiceInfo> cache) {
+ final ServiceInfo service = getServiceInfo(jobStatus, cache);
+
+ if (DEBUG && service == null) {
+ Slog.v(TAG, jobStatus.toShortString() + " component not present");
+ }
+ final ServiceInfo ogService = jobStatus.serviceInfo;
+ jobStatus.serviceInfo = service;
+ return !Objects.equals(ogService, service);
+ }
+
+ private void updateComponentStateForPackage(final String pkg) {
+ updateComponentStates(
+ jobStatus -> jobStatus.getServiceComponent().getPackageName().equals(pkg));
+ }
+
+ private void updateComponentStateForUser(final int userId) {
+ updateComponentStates(jobStatus -> {
+ // Using user ID instead of source user ID because the service will run under the
+ // user ID, not source user ID.
+ return jobStatus.getUserId() == userId;
+ });
+ }
+
+ private void updateComponentStates(@NonNull Predicate<JobStatus> filter) {
+ synchronized (mLock) {
+ mComponentStateUpdateFunctor.reset();
+ mService.getJobStore().forEachJob(filter, mComponentStateUpdateFunctor);
+ if (mComponentStateUpdateFunctor.mChanged) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ }
+ }
+
+ final class ComponentStateUpdateFunctor implements Consumer<JobStatus> {
+ boolean mChanged;
+ final SparseArrayMap<ComponentName, ServiceInfo> mTempCache = new SparseArrayMap<>();
+
+ @Override
+ public void accept(JobStatus jobStatus) {
+ mChanged |= updateComponentEnabledStateLocked(jobStatus, mTempCache);
+ }
+
+ private void reset() {
+ mChanged = false;
+ mTempCache.clear();
+ }
+ }
+
+ @Override
+ public void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate) {
+
+ }
+
+ @Override
+ public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
+ Predicate<JobStatus> predicate) {
+
+ }
+}
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 d7be259..c7cc2f0 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
@@ -27,6 +27,7 @@
import android.app.job.JobWorkItem;
import android.content.ClipData;
import android.content.ComponentName;
+import android.content.pm.ServiceInfo;
import android.net.Network;
import android.net.Uri;
import android.os.RemoteException;
@@ -296,6 +297,7 @@
public ArraySet<Uri> changedUris;
public ArraySet<String> changedAuthorities;
public Network network;
+ public ServiceInfo serviceInfo;
public int lastEvaluatedPriority;
@@ -1284,8 +1286,8 @@
// run if its constraints are satisfied).
// DeviceNotDozing implicit constraint must be satisfied
// NotRestrictedInBackground implicit constraint must be satisfied
- return mReadyNotDozing && mReadyNotRestrictedInBg && (mReadyDeadlineSatisfied
- || isConstraintsSatisfied(satisfiedConstraints));
+ return mReadyNotDozing && mReadyNotRestrictedInBg && (serviceInfo != null)
+ && (mReadyDeadlineSatisfied || isConstraintsSatisfied(satisfiedConstraints));
}
/** All constraints besides implicit and deadline. */
@@ -1767,6 +1769,9 @@
pw.print(prefix);
pw.print(" readyDynamicSatisfied: ");
pw.println(mReadyDynamicSatisfied);
+ pw.print(prefix);
+ pw.print(" readyComponentEnabled: ");
+ pw.println(serviceInfo != null);
if (changedAuthorities != null) {
pw.print(prefix); pw.println("Changed authorities:");
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 4e37a1e..c06e19c 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
@@ -296,13 +296,14 @@
}
/** List of all tracked jobs keyed by source package-userId combo. */
- private final SparseArrayMap<ArraySet<JobStatus>> mTrackedJobs = new SparseArrayMap<>();
+ private final SparseArrayMap<String, ArraySet<JobStatus>> mTrackedJobs = new SparseArrayMap<>();
/** Timer for each package-userId combo. */
- private final SparseArrayMap<Timer> mPkgTimers = new SparseArrayMap<>();
+ private final SparseArrayMap<String, Timer> mPkgTimers = new SparseArrayMap<>();
/** List of all timing sessions for a package-userId combo, in chronological order. */
- private final SparseArrayMap<List<TimingSession>> mTimingSessions = new SparseArrayMap<>();
+ private final SparseArrayMap<String, List<TimingSession>> mTimingSessions =
+ new SparseArrayMap<>();
/**
* Listener to track and manage when each package comes back within quota.
@@ -311,7 +312,8 @@
private final InQuotaAlarmListener mInQuotaAlarmListener = new InQuotaAlarmListener();
/** Cached calculation results for each app, with the standby buckets as the array indices. */
- private final SparseArrayMap<ExecutionStats[]> mExecutionStatsCache = new SparseArrayMap<>();
+ private final SparseArrayMap<String, ExecutionStats[]> mExecutionStatsCache =
+ new SparseArrayMap<>();
/** List of UIDs currently in the foreground. */
private final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
@@ -1225,7 +1227,8 @@
}
private class UidConstraintUpdater implements Consumer<JobStatus> {
- private final SparseArrayMap<Integer> mToScheduleStartAlarms = new SparseArrayMap<>();
+ private final SparseArrayMap<String, Integer> mToScheduleStartAlarms =
+ new SparseArrayMap<>();
public boolean wasJobChanged;
@Override
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index c033138..22e1eec 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -1159,6 +1159,10 @@
if (isDeviceProvisioningPackage(packageName)) {
return STANDBY_BUCKET_EXEMPTED;
}
+
+ if (mInjector.isWellbeingPackage(packageName)) {
+ return STANDBY_BUCKET_WORKING_SET;
+ }
}
// Check this last, as it can be the most expensive check
@@ -1966,6 +1970,7 @@
*/
@GuardedBy("mPowerWhitelistedApps")
private final ArraySet<String> mPowerWhitelistedApps = new ArraySet<>();
+ private String mWellbeingApp = null;
Injector(Context context, Looper looper) {
mContext = context;
@@ -1999,6 +2004,9 @@
if (activityManager.isLowRamDevice() || ActivityManager.isSmallBatteryDevice()) {
mAutoRestrictedBucketDelayMs = 12 * ONE_HOUR;
}
+
+ final PackageManager packageManager = mContext.getPackageManager();
+ mWellbeingApp = packageManager.getWellbeingPackageName();
}
mBootPhase = phase;
}
@@ -2043,6 +2051,14 @@
}
}
+ /**
+ * Returns {@code true} if the supplied package is the wellbeing app. Otherwise,
+ * returns {@code false}.
+ */
+ boolean isWellbeingPackage(String packageName) {
+ return mWellbeingApp != null && mWellbeingApp.equals(packageName);
+ }
+
void updatePowerWhitelistCache() {
try {
// Don't call out to DeviceIdleController with the lock held.
diff --git a/api/current.txt b/api/current.txt
index 9b9e258..04c28176 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6188,7 +6188,7 @@
method public android.app.PictureInPictureParams build();
method public android.app.PictureInPictureParams.Builder setActions(java.util.List<android.app.RemoteAction>);
method public android.app.PictureInPictureParams.Builder setAspectRatio(android.util.Rational);
- method @NonNull public android.app.PictureInPictureParams.Builder setAutoEnterAllowed(boolean);
+ method @NonNull public android.app.PictureInPictureParams.Builder setAutoEnterEnabled(boolean);
method public android.app.PictureInPictureParams.Builder setSourceRectHint(android.graphics.Rect);
}
@@ -12061,6 +12061,7 @@
method public boolean getSyntheticAppDetailsActivityEnabled(@NonNull String);
method @NonNull public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
method @Nullable public abstract String[] getSystemSharedLibraryNames();
+ method @IntRange(from=0) public int getTargetSdkVersion(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract CharSequence getText(@NonNull String, @StringRes int, @Nullable android.content.pm.ApplicationInfo);
method @NonNull public abstract android.graphics.drawable.Drawable getUserBadgedDrawableForDensity(@NonNull android.graphics.drawable.Drawable, @NonNull android.os.UserHandle, @Nullable android.graphics.Rect, int);
method @NonNull public abstract android.graphics.drawable.Drawable getUserBadgedIcon(@NonNull android.graphics.drawable.Drawable, @NonNull android.os.UserHandle);
@@ -23925,6 +23926,7 @@
method @NonNull public java.util.List<java.lang.String> getAllProviders();
method @Nullable public String getBestProvider(@NonNull android.location.Criteria, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull String, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull String, @NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
method @NonNull public android.location.GnssCapabilities getGnssCapabilities();
method @Nullable public String getGnssHardwareModelName();
method public int getGnssYearOfHardware();
@@ -23959,6 +23961,8 @@
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(long, float, @NonNull android.location.Criteria, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull String, long, float, @NonNull android.app.PendingIntent);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(long, float, @NonNull android.location.Criteria, @NonNull android.app.PendingIntent);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull String, @NonNull android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull String, @NonNull android.location.LocationRequest, @NonNull android.app.PendingIntent);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull String, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull android.location.Criteria, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull String, @NonNull android.app.PendingIntent);
@@ -24002,6 +24006,30 @@
field @Deprecated public static final int TEMPORARILY_UNAVAILABLE = 1; // 0x1
}
+ public final class LocationRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=1) public long getDurationMillis();
+ method @IntRange(from=0) public long getIntervalMillis();
+ method @IntRange(from=1, to=java.lang.Integer.MAX_VALUE) public int getMaxUpdates();
+ method @FloatRange(from=0, to=java.lang.Float.MAX_VALUE) public float getMinUpdateDistanceMeters();
+ method @IntRange(from=0) public long getMinUpdateIntervalMillis();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.LocationRequest> CREATOR;
+ field public static final long PASSIVE_INTERVAL = 9223372036854775807L; // 0x7fffffffffffffffL
+ }
+
+ public static final class LocationRequest.Builder {
+ ctor public LocationRequest.Builder(long);
+ ctor public LocationRequest.Builder(@NonNull android.location.LocationRequest);
+ method @NonNull public android.location.LocationRequest build();
+ method @NonNull public android.location.LocationRequest.Builder clearMinUpdateIntervalMillis();
+ method @NonNull public android.location.LocationRequest.Builder setDurationMillis(@IntRange(from=1) long);
+ method @NonNull public android.location.LocationRequest.Builder setIntervalMillis(@IntRange(from=0) long);
+ method @NonNull public android.location.LocationRequest.Builder setMaxUpdates(@IntRange(from=1, to=java.lang.Integer.MAX_VALUE) int);
+ method @NonNull public android.location.LocationRequest.Builder setMinUpdateDistanceMeters(@FloatRange(from=0, to=java.lang.Float.MAX_VALUE) float);
+ method @NonNull public android.location.LocationRequest.Builder setMinUpdateIntervalMillis(@IntRange(from=0) long);
+ }
+
public interface OnNmeaMessageListener {
method public void onNmeaMessage(String, long);
}
@@ -27298,6 +27326,12 @@
field public static final android.media.MediaTimestamp TIMESTAMP_UNKNOWN;
}
+ public class MediaTranscodingException extends java.lang.Exception {
+ }
+
+ public static final class MediaTranscodingException.ServiceNotAvailableException extends android.media.MediaTranscodingException {
+ }
+
public interface MicrophoneDirection {
method public boolean setPreferredMicrophoneDirection(int);
method public boolean setPreferredMicrophoneFieldDimension(@FloatRange(from=-1.0, to=1.0) float);
@@ -46070,6 +46104,8 @@
field public static final int MISSED = 5; // 0x5
field public static final int OTHER = 9; // 0x9
field public static final String REASON_EMERGENCY_CALL_PLACED = "REASON_EMERGENCY_CALL_PLACED";
+ field public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
+ field public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
field public static final int REJECTED = 6; // 0x6
field public static final int REMOTE = 3; // 0x3
field public static final int RESTRICTED = 8; // 0x8
@@ -46164,6 +46200,7 @@
field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
field public static final int CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE = 256; // 0x100
field @NonNull public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
+ field public static final String EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE = "android.telecom.extra.ALWAYS_USE_VOIP_AUDIO_MODE";
field public static final String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING = "android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING";
field public static final String EXTRA_CALL_SUBJECT_MAX_LENGTH = "android.telecom.extra.CALL_SUBJECT_MAX_LENGTH";
field public static final String EXTRA_LOG_SELF_MANAGED_CALLS = "android.telecom.extra.LOG_SELF_MANAGED_CALLS";
@@ -46730,6 +46767,7 @@
field public static final String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
field public static final String KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY = "apn_settings_default_apn_types_string_array";
field public static final String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool";
+ field public static final String KEY_CALL_BARRING_DEFAULT_SERVICE_CLASS_INT = "call_barring_default_service_class_int";
field public static final String KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL = "call_barring_supports_deactivate_all_bool";
field public static final String KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL = "call_barring_supports_password_change_bool";
field public static final String KEY_CALL_BARRING_VISIBILITY_BOOL = "call_barring_visibility_bool";
@@ -46957,6 +46995,8 @@
field public static final String KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING = "wfc_emergency_address_carrier_app_string";
field public static final String KEY_WORLD_MODE_ENABLED_BOOL = "world_mode_enabled_bool";
field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
+ field public static final int SERVICE_CLASS_NONE = 0; // 0x0
+ field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
}
public static final class CarrierConfigManager.Apn {
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 4f0e2cd..50d68db 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -10,6 +10,10 @@
field public static final String ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED = "android.app.action.NOTIFICATION_LISTENER_ENABLED_CHANGED";
}
+ public class StatusBarManager {
+ method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSimNetworkLock(boolean);
+ }
+
}
package android.content.rollback {
diff --git a/api/system-current.txt b/api/system-current.txt
index 4134711..65fe85b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -671,6 +671,9 @@
method @Nullable public android.content.ComponentName getAllowedNotificationAssistant();
method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName);
method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean);
+ field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL";
+ field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL";
+ field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL";
}
public final class RuntimeAppOpAccessMessage implements android.os.Parcelable {
@@ -1589,12 +1592,16 @@
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setBluetoothTethering(boolean);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
+ field public static final String ACTION_TETHERING_STATE_CHANGED = "android.bluetooth.action.TETHERING_STATE_CHANGED";
field public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE";
+ field public static final String EXTRA_TETHERING_STATE = "android.bluetooth.extra.TETHERING_STATE";
field public static final int LOCAL_NAP_ROLE = 1; // 0x1
field public static final int LOCAL_PANU_ROLE = 2; // 0x2
field public static final int PAN_ROLE_NONE = 0; // 0x0
field public static final int REMOTE_NAP_ROLE = 1; // 0x1
field public static final int REMOTE_PANU_ROLE = 2; // 0x2
+ field public static final int TETHERING_STATE_OFF = 1; // 0x1
+ field public static final int TETHERING_STATE_ON = 2; // 0x2
}
public class BluetoothPbap implements android.bluetooth.BluetoothProfile {
@@ -4075,7 +4082,7 @@
public class LocationManager {
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch();
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
method @Nullable public String getExtraLocationControllerPackage();
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int getGnssBatchSize();
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void injectGnssMeasurementCorrections(@NonNull android.location.GnssMeasurementCorrections);
@@ -4086,9 +4093,9 @@
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.LOCATION_HARDWARE}) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackage(@Nullable String);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackageEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle);
@@ -4097,42 +4104,49 @@
}
public final class LocationRequest implements android.os.Parcelable {
- method @NonNull public static android.location.LocationRequest create();
- method @NonNull public static android.location.LocationRequest createFromDeprecatedCriteria(@NonNull android.location.Criteria, long, float, boolean);
- method @NonNull public static android.location.LocationRequest createFromDeprecatedProvider(@NonNull String, long, float, boolean);
- method public int describeContents();
+ method @Deprecated @NonNull public static android.location.LocationRequest create();
+ method @Deprecated @NonNull public static android.location.LocationRequest createFromDeprecatedCriteria(@NonNull android.location.Criteria, long, float, boolean);
+ method @Deprecated @NonNull public static android.location.LocationRequest createFromDeprecatedProvider(@NonNull String, long, float, boolean);
method @Deprecated public long getExpireAt();
- method public long getExpireIn();
- method public long getFastestInterval();
- method public boolean getHideFromAppOps();
- method public long getInterval();
- method public int getNumUpdates();
- method @NonNull public String getProvider();
+ method @Deprecated public long getExpireIn();
+ method @Deprecated public long getFastestInterval();
+ method @Deprecated public boolean getHideFromAppOps();
+ method @Deprecated public long getInterval();
+ method @Deprecated public int getNumUpdates();
+ method @Deprecated @NonNull public String getProvider();
method public int getQuality();
- method public float getSmallestDisplacement();
+ method @Deprecated public float getSmallestDisplacement();
method @Nullable public android.os.WorkSource getWorkSource();
+ method public boolean isHiddenFromAppOps();
method public boolean isLocationSettingsIgnored();
- method public boolean isLowPowerMode();
+ method public boolean isLowPower();
+ method @Deprecated public boolean isLowPowerMode();
method @Deprecated @NonNull public android.location.LocationRequest setExpireAt(long);
- method @NonNull public android.location.LocationRequest setExpireIn(long);
- method @NonNull public android.location.LocationRequest setFastestInterval(long);
- method public void setHideFromAppOps(boolean);
- method @NonNull public android.location.LocationRequest setInterval(long);
- method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest setLocationSettingsIgnored(boolean);
- method @NonNull public android.location.LocationRequest setLowPowerMode(boolean);
- method @NonNull public android.location.LocationRequest setNumUpdates(int);
- method @NonNull public android.location.LocationRequest setProvider(@NonNull String);
- method @NonNull public android.location.LocationRequest setQuality(int);
- method @NonNull public android.location.LocationRequest setSmallestDisplacement(float);
- method public void setWorkSource(@Nullable android.os.WorkSource);
- method public void writeToParcel(android.os.Parcel, int);
+ method @Deprecated @NonNull public android.location.LocationRequest setExpireIn(long);
+ method @Deprecated @NonNull public android.location.LocationRequest setFastestInterval(long);
+ method @Deprecated public void setHideFromAppOps(boolean);
+ method @Deprecated @NonNull public android.location.LocationRequest setInterval(long);
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest setLocationSettingsIgnored(boolean);
+ method @Deprecated @NonNull public android.location.LocationRequest setLowPowerMode(boolean);
+ method @Deprecated @NonNull public android.location.LocationRequest setNumUpdates(int);
+ method @Deprecated @NonNull public android.location.LocationRequest setProvider(@NonNull String);
+ method @Deprecated @NonNull public android.location.LocationRequest setQuality(int);
+ method @Deprecated @NonNull public android.location.LocationRequest setSmallestDisplacement(float);
+ method @Deprecated public void setWorkSource(@Nullable android.os.WorkSource);
field public static final int ACCURACY_BLOCK = 102; // 0x66
field public static final int ACCURACY_CITY = 104; // 0x68
field public static final int ACCURACY_FINE = 100; // 0x64
- field @NonNull public static final android.os.Parcelable.Creator<android.location.LocationRequest> CREATOR;
field public static final int POWER_HIGH = 203; // 0xcb
field public static final int POWER_LOW = 201; // 0xc9
- field public static final int POWER_NONE = 200; // 0xc8
+ field @Deprecated public static final int POWER_NONE = 200; // 0xc8
+ }
+
+ public static final class LocationRequest.Builder {
+ method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setLocationSettingsIgnored(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.location.LocationRequest.Builder setLowPower(boolean);
+ method @NonNull public android.location.LocationRequest.Builder setQuality(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
}
}
@@ -4353,7 +4367,7 @@
}
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;
+ 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;
field public static final int PRIORITY_REALTIME = 1; // 0x1
field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1
}
@@ -4368,7 +4382,6 @@
method @IntRange(from=0, to=100) public int getProgress();
method public int getResult();
method public int getStatus();
- method public boolean retry();
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
@@ -7195,6 +7208,7 @@
method public int getBandwidth();
method @Nullable public android.net.MacAddress getBssid();
method public int getFrequency();
+ method public int getWifiStandard();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int CHANNEL_WIDTH_160MHZ = 6; // 0x6
field public static final int CHANNEL_WIDTH_20MHZ = 2; // 0x2
@@ -9364,6 +9378,7 @@
field public static final String APN_SET_ID = "apn_set_id";
field public static final int CARRIER_EDITED = 4; // 0x4
field public static final String EDITED_STATUS = "edited";
+ field public static final int MATCH_ALL_APN_SET_ID = -1; // 0xffffffff
field public static final String MAX_CONNECTIONS = "max_conns";
field public static final String MODEM_PERSIST = "modem_cognitive";
field public static final String MTU = "mtu";
@@ -10375,6 +10390,10 @@
method public final void addExistingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.Connection, @NonNull android.telecom.Conference);
}
+ public final class DisconnectCause implements android.os.Parcelable {
+ field public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
+ }
+
public abstract class InCallService extends android.app.Service {
method @Deprecated public android.telecom.Phone getPhone();
method @Deprecated public void onPhoneCreated(android.telecom.Phone);
@@ -10503,7 +10522,12 @@
}
public final class PhoneAccount implements android.os.Parcelable {
+ field public static final int CAPABILITY_EMERGENCY_CALLS_ONLY = 128; // 0x80
+ field public static final int CAPABILITY_EMERGENCY_PREFERRED = 8192; // 0x2000
+ field public static final int CAPABILITY_EMERGENCY_VIDEO_CALLING = 512; // 0x200
field public static final int CAPABILITY_MULTI_USER = 32; // 0x20
+ field public static final String EXTRA_PLAY_CALL_RECORDING_TONE = "android.telecom.extra.PLAY_CALL_RECORDING_TONE";
+ field public static final String EXTRA_SORT_ORDER = "android.telecom.extra.SORT_ORDER";
}
public static class PhoneAccount.Builder {
@@ -10589,10 +10613,20 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
method @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 void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle);
+ field public static final String ACTION_CURRENT_TTY_MODE_CHANGED = "android.telecom.action.CURRENT_TTY_MODE_CHANGED";
+ field public static final String ACTION_TTY_PREFERRED_MODE_CHANGED = "android.telecom.action.TTY_PREFERRED_MODE_CHANGED";
+ field public static final int CALL_SOURCE_EMERGENCY_DIALPAD = 1; // 0x1
+ field public static final int CALL_SOURCE_EMERGENCY_SHORTCUT = 2; // 0x2
+ field public static final int CALL_SOURCE_UNSPECIFIED = 0; // 0x0
field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
+ field public static final String EXTRA_CALL_SOURCE = "android.telecom.extra.CALL_SOURCE";
+ field public static final String EXTRA_CALL_TECHNOLOGY_TYPE = "android.telecom.extra.CALL_TECHNOLOGY_TYPE";
field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
field public static final String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
+ field public static final String EXTRA_CURRENT_TTY_MODE = "android.telecom.extra.CURRENT_TTY_MODE";
field public static final String EXTRA_IS_USER_INTENT_EMERGENCY_CALL = "android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL";
+ field public static final String EXTRA_TTY_PREFERRED_MODE = "android.telecom.extra.TTY_PREFERRED_MODE";
+ field public static final String EXTRA_UNKNOWN_CALL_HANDLE = "android.telecom.extra.UNKNOWN_CALL_HANDLE";
field public static final int TTY_MODE_FULL = 1; // 0x1
field public static final int TTY_MODE_HCO = 2; // 0x2
field public static final int TTY_MODE_OFF = 0; // 0x0
@@ -10902,7 +10936,8 @@
method public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
- method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
+ method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
+ method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
method public void onPhysicalChannelConfigurationChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
method public void onRadioPowerStateChanged(int);
@@ -11521,6 +11556,7 @@
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();
@@ -11532,6 +11568,11 @@
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 = 2; // 0x2
+ field public static final int HANDOVER_FAILURE_MODE_LEGACY = 1; // 0x1
+ field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER = 3; // 0x3
+ field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 4; // 0x4
+ field public static final int HANDOVER_FAILURE_MODE_UNKNOWN = 0; // 0x0
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
@@ -11545,6 +11586,7 @@
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);
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index 9c40b6c..3a9f49c 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -1,43 +1,48 @@
// Baseline format: 1.0
AcronymName: android.net.NetworkCapabilities#setSSID(String):
- Acronyms should not be capitalized in method names: was `setSSID`, should this be `setSsid`?
+
ActionValue: android.location.Location#EXTRA_NO_GPS_LOCATION:
+ActionValue: android.net.TetheringManager#ACTION_TETHER_STATE_CHANGED:
+
+ActionValue: android.net.TetheringManager#EXTRA_ACTIVE_TETHER:
+
+ActionValue: android.net.TetheringManager#EXTRA_AVAILABLE_TETHER:
+
+ActionValue: android.net.TetheringManager#EXTRA_ERRORED_TETHER:
+
ActionValue: android.net.wifi.WifiManager#ACTION_LINK_CONFIGURATION_CHANGED:
-// Tethering broadcast action / extras cannot change name for backwards compatibility
-ActionValue: android.net.TetheringManager#ACTION_TETHER_STATE_CHANGED:
- Inconsistent action value; expected `android.net.action.TETHER_STATE_CHANGED`, was `android.net.conn.TETHER_STATE_CHANGED`
-ActionValue: android.net.TetheringManager#EXTRA_ACTIVE_TETHER:
- Inconsistent extra value; expected `android.net.extra.ACTIVE_TETHER`, was `tetherArray`
-ActionValue: android.net.TetheringManager#EXTRA_AVAILABLE_TETHER:
- Inconsistent extra value; expected `android.net.extra.AVAILABLE_TETHER`, was `availableArray`
-ActionValue: android.net.TetheringManager#EXTRA_ERRORED_TETHER:
- Inconsistent extra value; expected `android.net.extra.ERRORED_TETHER`, was `erroredArray`
ArrayReturn: android.bluetooth.BluetoothCodecStatus#BluetoothCodecStatus(android.bluetooth.BluetoothCodecConfig, android.bluetooth.BluetoothCodecConfig[], android.bluetooth.BluetoothCodecConfig[]) parameter #1:
- Method parameter should be Collection<BluetoothCodecConfig> (or subclass) instead of raw array; was `android.bluetooth.BluetoothCodecConfig[]`
+
ArrayReturn: android.bluetooth.BluetoothCodecStatus#BluetoothCodecStatus(android.bluetooth.BluetoothCodecConfig, android.bluetooth.BluetoothCodecConfig[], android.bluetooth.BluetoothCodecConfig[]) parameter #2:
- Method parameter should be Collection<BluetoothCodecConfig> (or subclass) instead of raw array; was `android.bluetooth.BluetoothCodecConfig[]`
+
ArrayReturn: android.bluetooth.BluetoothCodecStatus#getCodecsLocalCapabilities():
- Method should return Collection<BluetoothCodecConfig> (or subclass) instead of raw array; was `android.bluetooth.BluetoothCodecConfig[]`
+
ArrayReturn: android.bluetooth.BluetoothCodecStatus#getCodecsSelectableCapabilities():
- Method should return Collection<BluetoothCodecConfig> (or subclass) instead of raw array; was `android.bluetooth.BluetoothCodecConfig[]`
+
ArrayReturn: android.bluetooth.BluetoothUuid#containsAnyUuid(android.os.ParcelUuid[], android.os.ParcelUuid[]) parameter #0:
- Method parameter should be Collection<ParcelUuid> (or subclass) instead of raw array; was `android.os.ParcelUuid[]`
+
ArrayReturn: android.bluetooth.BluetoothUuid#containsAnyUuid(android.os.ParcelUuid[], android.os.ParcelUuid[]) parameter #1:
- Method parameter should be Collection<ParcelUuid> (or subclass) instead of raw array; was `android.os.ParcelUuid[]`
+
ArrayReturn: android.media.tv.tuner.Tuner.FilterCallback#onFilterEvent(android.media.tv.tuner.Tuner.Filter, android.media.tv.tuner.filter.FilterEvent[]) parameter #1:
- Method parameter should be Collection<FilterEvent> (or subclass) instead of raw array; was `android.media.tv.tuner.filter.FilterEvent[]`
+
ArrayReturn: android.net.NetworkScoreManager#requestScores(android.net.NetworkKey[]) parameter #0:
- Method parameter should be Collection<NetworkKey> (or subclass) instead of raw array; was `android.net.NetworkKey[]`
+
ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions():
+BuilderSetStyle: android.net.IpSecTransform.Builder#buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex):
+ Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.IpSecTransform.Builder.buildTunnelModeTransform(java.net.InetAddress,android.net.IpSecManager.SecurityParameterIndex)
+
+
+ExecutorRegistration: android.media.MediaPlayer#setOnImsRxNoticeListener(android.media.MediaPlayer.OnImsRxNoticeListener, android.os.Handler):
+ Registration methods should have overload that accepts delivery Executor: `setOnImsRxNoticeListener`
ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#deletePersistentGroup(android.net.wifi.p2p.WifiP2pManager.Channel, int, android.net.wifi.p2p.WifiP2pManager.ActionListener):
ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#factoryReset(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener):
@@ -65,7 +70,7 @@
IntentBuilderName: android.content.Context#registerReceiverForAllUsers(android.content.BroadcastReceiver, android.content.IntentFilter, String, android.os.Handler):
- Methods creating an Intent should be named `create<Foo>Intent()`, was `registerReceiverForAllUsers`
+
KotlinKeyword: android.app.Notification#when:
@@ -73,7 +78,19 @@
KotlinOperator: android.telephony.CbGeoUtils.Geometry#contains(android.telephony.CbGeoUtils.LatLng):
- Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+
+
+
+MissingGetterMatchingBuilder: android.net.wifi.rtt.RangingRequest.Builder#addResponder(android.net.wifi.rtt.ResponderConfig):
+ android.net.wifi.rtt.RangingRequest does not declare a `getResponders()` method matching method android.net.wifi.rtt.RangingRequest.Builder.addResponder(android.net.wifi.rtt.ResponderConfig)
+MissingGetterMatchingBuilder: android.security.keystore.KeyGenParameterSpec.Builder#setUid(int):
+ android.security.keystore.KeyGenParameterSpec does not declare a `getUid()` method matching method android.security.keystore.KeyGenParameterSpec.Builder.setUid(int)
+MissingGetterMatchingBuilder: android.service.autofill.Dataset.Builder#setFieldInlinePresentation(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.service.autofill.InlinePresentation):
+ android.service.autofill.Dataset does not declare a `getFieldInlinePresentation()` method matching method android.service.autofill.Dataset.Builder.setFieldInlinePresentation(android.view.autofill.AutofillId,android.view.autofill.AutofillValue,java.util.regex.Pattern,android.service.autofill.InlinePresentation)
+MissingGetterMatchingBuilder: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallViaAudioProcessing(boolean):
+ android.telecom.CallScreeningService.CallResponse does not declare a `shouldScreenCallViaAudioProcessing()` method matching method android.telecom.CallScreeningService.CallResponse.Builder.setShouldScreenCallViaAudioProcessing(boolean)
+MissingGetterMatchingBuilder: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
+ android.telephony.mbms.DownloadRequest does not declare a `getServiceId()` method matching method android.telephony.mbms.DownloadRequest.Builder.setServiceId(String)
MissingNullability: android.hardware.soundtrigger.SoundTrigger.ModuleProperties#toString():
@@ -217,28 +234,27 @@
NoClone: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0:
-
+
NoSettingsProvider: android.provider.Settings.Global#TETHER_OFFLOAD_DISABLED:
- New setting keys are not allowed. (Field: TETHER_OFFLOAD_DISABLED)
+
NoSettingsProvider: android.provider.Settings.Global#TETHER_SUPPORTED:
- New setting keys are not allowed. (Field: TETHER_SUPPORTED)
NotCloseable: android.bluetooth.BluetoothA2dpSink:
- Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.bluetooth.BluetoothA2dpSink
+
NotCloseable: android.bluetooth.BluetoothMap:
- Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.bluetooth.BluetoothMap
+
NotCloseable: android.bluetooth.BluetoothPan:
- Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.bluetooth.BluetoothPan
+
NotCloseable: android.bluetooth.BluetoothPbap:
- Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.bluetooth.BluetoothPbap
+
OnNameExpected: android.content.ContentProvider#checkUriPermission(android.net.Uri, int, int):
- If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+
PairedRegistration: android.net.wifi.nl80211.WifiNl80211Manager#registerApCallback(String, java.util.concurrent.Executor, android.net.wifi.nl80211.WifiNl80211Manager.SoftApCallback):
@@ -300,7 +316,7 @@
SamShouldBeLast: android.app.WallpaperInfo#dump(android.util.Printer, String):
SamShouldBeLast: android.app.WallpaperManager#addOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener, android.os.Handler):
- SAM-compatible parameters (such as parameter 1, "listener", in android.app.WallpaperManager.addOnColorsChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+
SamShouldBeLast: android.app.admin.DevicePolicyManager#installSystemUpdate(android.content.ComponentName, android.net.Uri, java.util.concurrent.Executor, android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback):
SamShouldBeLast: android.content.Context#bindIsolatedService(android.content.Intent, int, String, java.util.concurrent.Executor, android.content.ServiceConnection):
@@ -334,19 +350,19 @@
SamShouldBeLast: android.location.LocationManager#registerGnssStatusCallback(java.util.concurrent.Executor, android.location.GnssStatus.Callback):
SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, android.location.LocationListener, android.os.Looper):
- SAM-compatible parameters (such as parameter 4, "listener", in android.location.LocationManager.requestLocationUpdates) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+
SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, java.util.concurrent.Executor, android.location.LocationListener):
SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, java.util.concurrent.Executor, android.location.LocationListener):
SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(long, float, android.location.Criteria, android.location.LocationListener, android.os.Looper):
- SAM-compatible parameters (such as parameter 4, "listener", in android.location.LocationManager.requestLocationUpdates) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+
SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(long, float, android.location.Criteria, java.util.concurrent.Executor, android.location.LocationListener):
SamShouldBeLast: android.location.LocationManager#requestSingleUpdate(String, android.location.LocationListener, android.os.Looper):
- SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestSingleUpdate) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+
SamShouldBeLast: android.location.LocationManager#requestSingleUpdate(android.location.Criteria, android.location.LocationListener, android.os.Looper):
- SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestSingleUpdate) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+
SamShouldBeLast: android.media.AudioFocusRequest.Builder#setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener, android.os.Handler):
SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int):
@@ -359,6 +375,10 @@
SamShouldBeLast: android.media.AudioRouting#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
+SamShouldBeLast: android.media.AudioTrack#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioTrack.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.MediaPlayer#setOnImsRxNoticeListener(android.media.MediaPlayer.OnImsRxNoticeListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.media.MediaPlayer.setOnImsRxNoticeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.media.MediaRecorder#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
SamShouldBeLast: android.media.MediaRecorder#registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback):
@@ -510,8 +530,8 @@
UserHandle: android.companion.CompanionDeviceManager#isDeviceAssociated(String, android.net.MacAddress, android.os.UserHandle):
- When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
+
UserHandleName: android.telephony.CellBroadcastIntents#sendOrderedBroadcastForBackgroundReceivers(android.content.Context, android.os.UserHandle, android.content.Intent, String, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle):
- Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `sendOrderedBroadcastForBackgroundReceivers`
+
diff --git a/api/test-current.txt b/api/test-current.txt
index de2919b..7a7cdd1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -156,8 +156,6 @@
public class ActivityView extends android.view.ViewGroup {
ctor public ActivityView(android.content.Context);
ctor public ActivityView(android.content.Context, android.util.AttributeSet);
- ctor public ActivityView(android.content.Context, android.util.AttributeSet, int);
- ctor public ActivityView(android.content.Context, android.util.AttributeSet, int, boolean);
ctor public ActivityView(@NonNull android.content.Context, @NonNull android.util.AttributeSet, int, boolean, boolean);
method public int getVirtualDisplayId();
method public void onLayout(boolean, int, int, int, int);
@@ -172,6 +170,17 @@
method public void startActivity(@NonNull android.app.PendingIntent, @Nullable android.content.Intent, @NonNull android.app.ActivityOptions);
}
+ public static final class ActivityView.Builder {
+ ctor public ActivityView.Builder(@NonNull android.content.Context);
+ method @NonNull public android.app.ActivityView build();
+ method @NonNull public android.app.ActivityView.Builder setAttributeSet(@Nullable android.util.AttributeSet);
+ method @NonNull public android.app.ActivityView.Builder setDefaultStyle(int);
+ method @NonNull public android.app.ActivityView.Builder setDisableSurfaceViewBackgroundLayer(boolean);
+ method @NonNull public android.app.ActivityView.Builder setSingleInstance(boolean);
+ method @NonNull public android.app.ActivityView.Builder setUsePublicVirtualDisplay(boolean);
+ method @NonNull public android.app.ActivityView.Builder setUseTrustedDisplay(boolean);
+ }
+
public abstract static class ActivityView.StateCallback {
ctor public ActivityView.StateCallback();
method public abstract void onActivityViewDestroyed(android.app.ActivityView);
@@ -1686,45 +1695,34 @@
public class LocationManager {
method @NonNull public String[] getBackgroundThrottlingWhitelist();
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
method @NonNull public String[] getIgnoreSettingsWhitelist();
method @Deprecated @Nullable @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public java.util.List<java.lang.String> getProviderPackages(@NonNull String);
method @NonNull public java.util.List<android.location.LocationRequest> getTestProviderCurrentRequests(String);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle);
field public static final String FUSED_PROVIDER = "fused";
}
public final class LocationRequest implements android.os.Parcelable {
- method @NonNull public static android.location.LocationRequest create();
- method public int describeContents();
- method @Deprecated public long getExpireAt();
- method public long getExpireIn();
- method public long getFastestInterval();
- method public long getInterval();
- method public int getNumUpdates();
- method public int getQuality();
+ method @Nullable public android.os.WorkSource getWorkSource();
+ method public boolean isHiddenFromAppOps();
method public boolean isLocationSettingsIgnored();
- method public boolean isLowPowerMode();
- method @Deprecated @NonNull public android.location.LocationRequest setExpireAt(long);
- method @NonNull public android.location.LocationRequest setExpireIn(long);
- method @NonNull public android.location.LocationRequest setFastestInterval(long);
- method @NonNull public android.location.LocationRequest setInterval(long);
- method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest setLocationSettingsIgnored(boolean);
- method @NonNull public android.location.LocationRequest setLowPowerMode(boolean);
- method @NonNull public android.location.LocationRequest setNumUpdates(int);
- method @NonNull public android.location.LocationRequest setProvider(@NonNull String);
- method @NonNull public android.location.LocationRequest setQuality(int);
- method public void writeToParcel(android.os.Parcel, int);
+ method public boolean isLowPower();
field public static final int ACCURACY_BLOCK = 102; // 0x66
field public static final int ACCURACY_CITY = 104; // 0x68
field public static final int ACCURACY_FINE = 100; // 0x64
- field @NonNull public static final android.os.Parcelable.Creator<android.location.LocationRequest> CREATOR;
field public static final int POWER_HIGH = 203; // 0xcb
field public static final int POWER_LOW = 201; // 0xc9
- field public static final int POWER_NONE = 200; // 0xc8
+ }
+
+ public static final class LocationRequest.Builder {
+ method @NonNull @RequiresPermission("android.permission.UPDATE_APP_OPS_STATS") public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setLocationSettingsIgnored(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.location.LocationRequest.Builder setLowPower(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
}
}
@@ -1819,7 +1817,7 @@
}
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;
+ 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;
field public static final int PRIORITY_REALTIME = 1; // 0x1
field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1
}
@@ -1834,7 +1832,6 @@
method @IntRange(from=0, to=100) public int getProgress();
method public int getResult();
method public int getStatus();
- method public boolean retry();
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
@@ -4004,6 +4001,10 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
method @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 void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle);
+ field public static final String ACTION_CURRENT_TTY_MODE_CHANGED = "android.telecom.action.CURRENT_TTY_MODE_CHANGED";
+ field public static final String ACTION_TTY_PREFERRED_MODE_CHANGED = "android.telecom.action.TTY_PREFERRED_MODE_CHANGED";
+ field public static final String EXTRA_CURRENT_TTY_MODE = "android.telecom.extra.CURRENT_TTY_MODE";
+ field public static final String EXTRA_TTY_PREFERRED_MODE = "android.telecom.extra.TTY_PREFERRED_MODE";
field public static final int TTY_MODE_FULL = 1; // 0x1
field public static final int TTY_MODE_HCO = 2; // 0x2
field public static final int TTY_MODE_OFF = 0; // 0x0
@@ -4137,7 +4138,8 @@
public class PhoneStateListener {
method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
- method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
+ method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
+ method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
}
@@ -5087,23 +5089,23 @@
field public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
}
- public class SparseArrayMap<T> {
+ public class SparseArrayMap<K, V> {
ctor public SparseArrayMap();
- method public void add(int, @NonNull String, @Nullable T);
+ method public void add(int, @NonNull K, @Nullable V);
method public void clear();
- method public boolean contains(int, @NonNull String);
+ method public boolean contains(int, @NonNull K);
method public void delete(int);
- method @Nullable public T delete(int, @NonNull String);
- method public void forEach(@NonNull java.util.function.Consumer<T>);
- method @Nullable public T get(int, @NonNull String);
- method @Nullable public T getOrDefault(int, @NonNull String, T);
+ method @Nullable public V delete(int, @NonNull K);
+ method public void forEach(@NonNull java.util.function.Consumer<V>);
+ method @Nullable public V get(int, @NonNull K);
+ method @Nullable public V getOrDefault(int, @NonNull K, V);
method public int indexOfKey(int);
- method public int indexOfKey(int, @NonNull String);
+ method public int indexOfKey(int, @NonNull K);
method public int keyAt(int);
- method @NonNull public String keyAt(int, int);
+ method @NonNull public K keyAt(int, int);
method public int numElementsForKey(int);
method public int numMaps();
- method @Nullable public T valueAt(int, int);
+ method @Nullable public V valueAt(int, int);
}
public class TimeUtils {
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index 63ba4aa..91a09e3 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -74,11 +74,11 @@
ArrayReturn: android.app.UiAutomation#executeShellCommandRw(String):
ArrayReturn: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#KeyphraseSoundModel(java.util.UUID, java.util.UUID, byte[], android.hardware.soundtrigger.SoundTrigger.Keyphrase[]) parameter #3:
- Method parameter should be Collection<Keyphrase> (or subclass) instead of raw array; was `android.hardware.soundtrigger.SoundTrigger.Keyphrase[]`
+
ArrayReturn: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#KeyphraseSoundModel(java.util.UUID, java.util.UUID, byte[], android.hardware.soundtrigger.SoundTrigger.Keyphrase[], int) parameter #3:
- Method parameter should be Collection<Keyphrase> (or subclass) instead of raw array; was `android.hardware.soundtrigger.SoundTrigger.Keyphrase[]`
+
ArrayReturn: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#getKeyphrases():
- Method should return Collection<Keyphrase> (or subclass) instead of raw array; was `android.hardware.soundtrigger.SoundTrigger.Keyphrase[]`
+
ArrayReturn: android.location.GnssMeasurementsEvent#GnssMeasurementsEvent(android.location.GnssClock, android.location.GnssMeasurement[]) parameter #1:
ArrayReturn: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #10:
@@ -218,33 +218,33 @@
BuilderSetStyle: android.media.audiopolicy.AudioMixingRule.Builder#allowPrivilegedPlaybackCapture(boolean):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.media.audiopolicy.AudioMixingRule.Builder.allowPrivilegedPlaybackCapture(boolean)
+
BuilderSetStyle: android.media.audiopolicy.AudioMixingRule.Builder#excludeMixRule(int, Object):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.media.audiopolicy.AudioMixingRule.Builder.excludeMixRule(int,Object)
+
BuilderSetStyle: android.media.audiopolicy.AudioMixingRule.Builder#excludeRule(android.media.AudioAttributes, int):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.media.audiopolicy.AudioMixingRule.Builder.excludeRule(android.media.AudioAttributes,int)
+
BuilderSetStyle: android.net.NetworkCapabilities.Builder#removeCapability(int):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.NetworkCapabilities.Builder.removeCapability(int)
+
BuilderSetStyle: android.net.NetworkCapabilities.Builder#removeTransportType(int):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.NetworkCapabilities.Builder.removeTransportType(int)
+
BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateDnsslLifetime(long):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updateDnsslLifetime(long)
+
BuilderSetStyle: android.net.metrics.RaEvent.Builder#updatePrefixPreferredLifetime(long):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updatePrefixPreferredLifetime(long)
+
BuilderSetStyle: android.net.metrics.RaEvent.Builder#updatePrefixValidLifetime(long):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updatePrefixValidLifetime(long)
+
BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateRdnssLifetime(long):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updateRdnssLifetime(long)
+
BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateRouteInfoLifetime(long):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updateRouteInfoLifetime(long)
+
BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateRouterLifetime(long):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updateRouterLifetime(long)
+
BuilderSetStyle: android.os.StrictMode.ThreadPolicy.Builder#detectExplicitGc():
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.ThreadPolicy.Builder.detectExplicitGc()
+
BuilderSetStyle: android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse():
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.VmPolicy.Builder.detectIncorrectContextUse()
+
BuilderSetStyle: android.os.StrictMode.VmPolicy.Builder#permitIncorrectContextUse():
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.VmPolicy.Builder.permitIncorrectContextUse()
+
CallbackInterface: android.app.prediction.AppPredictor.Callback:
@@ -382,11 +382,11 @@
ExecutorRegistration: android.os.RemoteCallback#RemoteCallback(android.os.RemoteCallback.OnResultListener, android.os.Handler):
ExecutorRegistration: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler):
- Registration methods should have overload that accepts delivery Executor: `countPermissionApps`
+
ExecutorRegistration: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler):
ExecutorRegistration: android.service.watchdog.ExplicitHealthCheckService#setCallback(android.os.RemoteCallback):
- Registration methods should have overload that accepts delivery Executor: `setCallback`
+
ExecutorRegistration: android.telephony.ims.stub.ImsCallSessionImplBase#setListener(android.telephony.ims.ImsCallSessionListener):
ExecutorRegistration: android.telephony.ims.stub.ImsUtImplBase#setListener(android.telephony.ims.ImsUtListener):
@@ -406,7 +406,7 @@
ExecutorRegistration: android.telephony.mbms.vendor.MbmsStreamingServiceBase#startStreaming(int, String, android.telephony.mbms.StreamingServiceCallback):
ExecutorRegistration: android.window.WindowOrganizer#applySyncTransaction(android.window.WindowContainerTransaction, android.window.WindowContainerTransactionCallback):
- Registration methods should have overload that accepts delivery Executor: `applySyncTransaction`
+
ForbiddenSuperClass: android.app.AppDetailsActivity:
@@ -428,9 +428,9 @@
GetterOnBuilder: android.hardware.display.BrightnessConfiguration.Builder#getMaxCorrectionsByCategory():
- Getter should be on the built object, not the builder: method android.hardware.display.BrightnessConfiguration.Builder.getMaxCorrectionsByCategory()
+
GetterOnBuilder: android.hardware.display.BrightnessConfiguration.Builder#getMaxCorrectionsByPackageName():
- Getter should be on the built object, not the builder: method android.hardware.display.BrightnessConfiguration.Builder.getMaxCorrectionsByPackageName()
+
GetterSetterNames: android.app.NotificationChannel#isBlockableSystem():
@@ -510,7 +510,7 @@
IntentBuilderName: android.app.backup.BackupManager#getDataManagementIntent(String):
IntentBuilderName: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getManageKeyphraseIntent(int, String, java.util.Locale):
- Methods creating an Intent should be named `create<Foo>Intent()`, was `getManageKeyphraseIntent`
+
IntentName: android.provider.Settings.Secure#VOICE_INTERACTION_SERVICE:
@@ -560,7 +560,7 @@
ListenerLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, android.location.LocationListener, android.os.Looper) parameter #2:
ListenerLast: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler) parameter #3:
- Listeners should always be at end of argument list (method `countPermissionApps`)
+
ListenerLast: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler) parameter #2:
ListenerLast: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) parameter #1:
@@ -593,140 +593,152 @@
+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):
- android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getAttributionTag()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setAttributionTag(String)
+
MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setFlags(int):
- android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getFlags()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setFlags(int)
+
MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setOpNames(java.util.List<java.lang.String>):
- android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getOpNames()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setOpNames(java.util.List<java.lang.String>)
+
MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setPackageName(String):
- android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getPackageName()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setPackageName(String)
+
MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setUid(int):
- android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getUid()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setUid(int)
+
MissingGetterMatchingBuilder: android.content.integrity.RuleSet.Builder#addRules(java.util.List<android.content.integrity.Rule>):
- android.content.integrity.RuleSet does not declare a `getRuless()` method matching method android.content.integrity.RuleSet.Builder.addRules(java.util.List<android.content.integrity.Rule>)
+
MissingGetterMatchingBuilder: android.hardware.display.BrightnessConfiguration.Builder#addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection):
- android.hardware.display.BrightnessConfiguration does not declare a `getCorrectionByCategorys()` method matching method android.hardware.display.BrightnessConfiguration.Builder.addCorrectionByCategory(int,android.hardware.display.BrightnessCorrection)
+
MissingGetterMatchingBuilder: android.hardware.display.BrightnessConfiguration.Builder#addCorrectionByPackageName(String, android.hardware.display.BrightnessCorrection):
- android.hardware.display.BrightnessConfiguration does not declare a `getCorrectionByPackageNames()` method matching method android.hardware.display.BrightnessConfiguration.Builder.addCorrectionByPackageName(String,android.hardware.display.BrightnessCorrection)
+
MissingGetterMatchingBuilder: android.hardware.display.BrightnessConfiguration.Builder#setDescription(String):
- android.hardware.display.BrightnessConfiguration does not declare a `getDescription()` method matching method android.hardware.display.BrightnessConfiguration.Builder.setDescription(String)
+
MissingGetterMatchingBuilder: android.hardware.lights.LightsRequest.Builder#setLight(android.hardware.lights.Light, android.hardware.lights.LightState):
- android.hardware.lights.LightsRequest does not declare a `getLight()` method matching method android.hardware.lights.LightsRequest.Builder.setLight(android.hardware.lights.Light,android.hardware.lights.LightState)
+
MissingGetterMatchingBuilder: android.media.VolumeShaper.Configuration.Builder#setOptionFlags(int):
- android.media.VolumeShaper.Configuration does not declare a `getOptionFlags()` method matching method android.media.VolumeShaper.Configuration.Builder.setOptionFlags(int)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMix.Builder#setDevice(android.media.AudioDeviceInfo):
- android.media.audiopolicy.AudioMix does not declare a `getDevice()` method matching method android.media.audiopolicy.AudioMix.Builder.setDevice(android.media.AudioDeviceInfo)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMix.Builder#setFormat(android.media.AudioFormat):
- android.media.audiopolicy.AudioMix does not declare a `getFormat()` method matching method android.media.audiopolicy.AudioMix.Builder.setFormat(android.media.AudioFormat)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMix.Builder#setRouteFlags(int):
- android.media.audiopolicy.AudioMix does not declare a `getRouteFlags()` method matching method android.media.audiopolicy.AudioMix.Builder.setRouteFlags(int)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMixingRule.Builder#addMixRule(int, Object):
- android.media.audiopolicy.AudioMixingRule does not declare a `getMixRules()` method matching method android.media.audiopolicy.AudioMixingRule.Builder.addMixRule(int,Object)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMixingRule.Builder#addRule(android.media.AudioAttributes, int):
- android.media.audiopolicy.AudioMixingRule does not declare a `getRules()` method matching method android.media.audiopolicy.AudioMixingRule.Builder.addRule(android.media.AudioAttributes,int)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#addMix(android.media.audiopolicy.AudioMix):
- android.media.audiopolicy.AudioPolicy does not declare a `getMixs()` method matching method android.media.audiopolicy.AudioPolicy.Builder.addMix(android.media.audiopolicy.AudioMix)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener):
- android.media.audiopolicy.AudioPolicy does not declare a `getAudioPolicyFocusListener()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener):
- android.media.audiopolicy.AudioPolicy does not declare a `getAudioPolicyStatusListener()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyVolumeCallback(android.media.audiopolicy.AudioPolicy.AudioPolicyVolumeCallback):
- android.media.audiopolicy.AudioPolicy does not declare a `getAudioPolicyVolumeCallback()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setAudioPolicyVolumeCallback(android.media.audiopolicy.AudioPolicy.AudioPolicyVolumeCallback)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setIsAudioFocusPolicy(boolean):
- android.media.audiopolicy.AudioPolicy does not declare a `isIsAudioFocusPolicy()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setIsAudioFocusPolicy(boolean)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setIsTestFocusPolicy(boolean):
- android.media.audiopolicy.AudioPolicy does not declare a `isIsTestFocusPolicy()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setIsTestFocusPolicy(boolean)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setLooper(android.os.Looper):
- android.media.audiopolicy.AudioPolicy does not declare a `getLooper()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setLooper(android.os.Looper)
+
MissingGetterMatchingBuilder: android.net.CaptivePortalData.Builder#setBytesRemaining(long):
- android.net.CaptivePortalData does not declare a `getBytesRemaining()` method matching method android.net.CaptivePortalData.Builder.setBytesRemaining(long)
+
MissingGetterMatchingBuilder: android.net.CaptivePortalData.Builder#setExpiryTime(long):
- android.net.CaptivePortalData does not declare a `getExpiryTime()` method matching method android.net.CaptivePortalData.Builder.setExpiryTime(long)
+
MissingGetterMatchingBuilder: android.net.CaptivePortalData.Builder#setRefreshTime(long):
- android.net.CaptivePortalData does not declare a `getRefreshTime()` method matching method android.net.CaptivePortalData.Builder.setRefreshTime(long)
+
MissingGetterMatchingBuilder: android.net.NetworkCapabilities.Builder#addCapability(int):
- android.net.NetworkCapabilities does not declare a `getCapabilitys()` method matching method android.net.NetworkCapabilities.Builder.addCapability(int)
+
MissingGetterMatchingBuilder: android.net.NetworkCapabilities.Builder#setRequestorPackageName(String):
- android.net.NetworkCapabilities does not declare a `getRequestorPackageName()` method matching method android.net.NetworkCapabilities.Builder.setRequestorPackageName(String)
+
MissingGetterMatchingBuilder: android.net.NetworkCapabilities.Builder#setRequestorUid(int):
- android.net.NetworkCapabilities does not declare a `getRequestorUid()` method matching method android.net.NetworkCapabilities.Builder.setRequestorUid(int)
+
MissingGetterMatchingBuilder: android.net.TetheringManager.TetheringRequest.Builder#setShouldShowEntitlementUi(boolean):
- android.net.TetheringManager.TetheringRequest does not declare a `shouldShowEntitlementUi()` method matching method android.net.TetheringManager.TetheringRequest.Builder.setShouldShowEntitlementUi(boolean)
+
MissingGetterMatchingBuilder: android.net.TetheringManager.TetheringRequest.Builder#setStaticIpv4Addresses(android.net.LinkAddress, android.net.LinkAddress):
- android.net.TetheringManager.TetheringRequest does not declare a `getStaticIpv4Addresses()` method matching method android.net.TetheringManager.TetheringRequest.Builder.setStaticIpv4Addresses(android.net.LinkAddress,android.net.LinkAddress)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setActualLifetime(long):
- android.net.metrics.ApfProgramEvent does not declare a `getActualLifetime()` method matching method android.net.metrics.ApfProgramEvent.Builder.setActualLifetime(long)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setCurrentRas(int):
- android.net.metrics.ApfProgramEvent does not declare a `getCurrentRas()` method matching method android.net.metrics.ApfProgramEvent.Builder.setCurrentRas(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setFilteredRas(int):
- android.net.metrics.ApfProgramEvent does not declare a `getFilteredRas()` method matching method android.net.metrics.ApfProgramEvent.Builder.setFilteredRas(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setFlags(boolean, boolean):
- android.net.metrics.ApfProgramEvent does not declare a `isFlags()` method matching method android.net.metrics.ApfProgramEvent.Builder.setFlags(boolean,boolean)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setLifetime(long):
- android.net.metrics.ApfProgramEvent does not declare a `getLifetime()` method matching method android.net.metrics.ApfProgramEvent.Builder.setLifetime(long)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setProgramLength(int):
- android.net.metrics.ApfProgramEvent does not declare a `getProgramLength()` method matching method android.net.metrics.ApfProgramEvent.Builder.setProgramLength(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setDroppedRas(int):
- android.net.metrics.ApfStats does not declare a `getDroppedRas()` method matching method android.net.metrics.ApfStats.Builder.setDroppedRas(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setDurationMs(long):
- android.net.metrics.ApfStats does not declare a `getDurationMs()` method matching method android.net.metrics.ApfStats.Builder.setDurationMs(long)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setMatchingRas(int):
- android.net.metrics.ApfStats does not declare a `getMatchingRas()` method matching method android.net.metrics.ApfStats.Builder.setMatchingRas(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setMaxProgramSize(int):
- android.net.metrics.ApfStats does not declare a `getMaxProgramSize()` method matching method android.net.metrics.ApfStats.Builder.setMaxProgramSize(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setParseErrors(int):
- android.net.metrics.ApfStats does not declare a `getParseErrors()` method matching method android.net.metrics.ApfStats.Builder.setParseErrors(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setProgramUpdates(int):
- android.net.metrics.ApfStats does not declare a `getProgramUpdates()` method matching method android.net.metrics.ApfStats.Builder.setProgramUpdates(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setProgramUpdatesAll(int):
- android.net.metrics.ApfStats does not declare a `getProgramUpdatesAll()` method matching method android.net.metrics.ApfStats.Builder.setProgramUpdatesAll(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setProgramUpdatesAllowingMulticast(int):
- android.net.metrics.ApfStats does not declare a `getProgramUpdatesAllowingMulticast()` method matching method android.net.metrics.ApfStats.Builder.setProgramUpdatesAllowingMulticast(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setReceivedRas(int):
- android.net.metrics.ApfStats does not declare a `getReceivedRas()` method matching method android.net.metrics.ApfStats.Builder.setReceivedRas(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setZeroLifetimeRas(int):
- android.net.metrics.ApfStats does not declare a `getZeroLifetimeRas()` method matching method android.net.metrics.ApfStats.Builder.setZeroLifetimeRas(int)
+
MissingGetterMatchingBuilder: android.net.metrics.DhcpClientEvent.Builder#setDurationMs(int):
- android.net.metrics.DhcpClientEvent does not declare a `getDurationMs()` method matching method android.net.metrics.DhcpClientEvent.Builder.setDurationMs(int)
+
MissingGetterMatchingBuilder: android.net.metrics.DhcpClientEvent.Builder#setMsg(String):
- android.net.metrics.DhcpClientEvent does not declare a `getMsg()` method matching method android.net.metrics.DhcpClientEvent.Builder.setMsg(String)
+
MissingGetterMatchingBuilder: android.net.metrics.ValidationProbeEvent.Builder#setDurationMs(long):
- android.net.metrics.ValidationProbeEvent does not declare a `getDurationMs()` method matching method android.net.metrics.ValidationProbeEvent.Builder.setDurationMs(long)
+
MissingGetterMatchingBuilder: android.net.metrics.ValidationProbeEvent.Builder#setProbeType(int, boolean):
- android.net.metrics.ValidationProbeEvent does not declare a `getProbeType()` method matching method android.net.metrics.ValidationProbeEvent.Builder.setProbeType(int,boolean)
+
MissingGetterMatchingBuilder: android.net.metrics.ValidationProbeEvent.Builder#setReturnCode(int):
- android.net.metrics.ValidationProbeEvent does not declare a `getReturnCode()` method matching method android.net.metrics.ValidationProbeEvent.Builder.setReturnCode(int)
+
MissingGetterMatchingBuilder: android.security.keystore.KeyGenParameterSpec.Builder#setUniqueIdIncluded(boolean):
- android.security.keystore.KeyGenParameterSpec does not declare a `isUniqueIdIncluded()` method matching method android.security.keystore.KeyGenParameterSpec.Builder.setUniqueIdIncluded(boolean)
+
MissingGetterMatchingBuilder: android.service.autofill.Dataset.Builder#setFieldInlinePresentation(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.service.autofill.InlinePresentation):
- android.service.autofill.Dataset does not declare a `getFieldInlinePresentation()` method matching method android.service.autofill.Dataset.Builder.setFieldInlinePresentation(android.view.autofill.AutofillId,android.view.autofill.AutofillValue,java.util.regex.Pattern,android.service.autofill.InlinePresentation)
+
MissingGetterMatchingBuilder: android.service.autofill.augmented.FillResponse.Builder#setClientState(android.os.Bundle):
- android.service.autofill.augmented.FillResponse does not declare a `getClientState()` method matching method android.service.autofill.augmented.FillResponse.Builder.setClientState(android.os.Bundle)
+
MissingGetterMatchingBuilder: android.service.autofill.augmented.FillResponse.Builder#setFillWindow(android.service.autofill.augmented.FillWindow):
- android.service.autofill.augmented.FillResponse does not declare a `getFillWindow()` method matching method android.service.autofill.augmented.FillResponse.Builder.setFillWindow(android.service.autofill.augmented.FillWindow)
+
MissingGetterMatchingBuilder: android.service.autofill.augmented.FillResponse.Builder#setInlineSuggestions(java.util.List<android.service.autofill.Dataset>):
- android.service.autofill.augmented.FillResponse does not declare a `getInlineSuggestions()` method matching method android.service.autofill.augmented.FillResponse.Builder.setInlineSuggestions(java.util.List<android.service.autofill.Dataset>)
+
MissingGetterMatchingBuilder: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallViaAudioProcessing(boolean):
- android.telecom.CallScreeningService.CallResponse does not declare a `shouldScreenCallViaAudioProcessing()` method matching method android.telecom.CallScreeningService.CallResponse.Builder.setShouldScreenCallViaAudioProcessing(boolean)
+
MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setIsAdhocConferenceCall(boolean):
- android.telecom.ConnectionRequest does not declare a `isIsAdhocConferenceCall()` method matching method android.telecom.ConnectionRequest.Builder.setIsAdhocConferenceCall(boolean)
+
MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setRttPipeFromInCall(android.os.ParcelFileDescriptor):
- android.telecom.ConnectionRequest does not declare a `getRttPipeFromInCall()` method matching method android.telecom.ConnectionRequest.Builder.setRttPipeFromInCall(android.os.ParcelFileDescriptor)
+
MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setRttPipeToInCall(android.os.ParcelFileDescriptor):
- android.telecom.ConnectionRequest does not declare a `getRttPipeToInCall()` method matching method android.telecom.ConnectionRequest.Builder.setRttPipeToInCall(android.os.ParcelFileDescriptor)
+
MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setShouldShowIncomingCallUi(boolean):
- android.telecom.ConnectionRequest does not declare a `shouldShowIncomingCallUi()` method matching method android.telecom.ConnectionRequest.Builder.setShouldShowIncomingCallUi(boolean)
+
MissingGetterMatchingBuilder: android.telecom.PhoneAccount.Builder#setGroupId(String):
- android.telecom.PhoneAccount does not declare a `getGroupId()` method matching method android.telecom.PhoneAccount.Builder.setGroupId(String)
+
MissingGetterMatchingBuilder: android.telephony.NetworkRegistrationInfo.Builder#setEmergencyOnly(boolean):
- android.telephony.NetworkRegistrationInfo does not declare a `isEmergencyOnly()` method matching method android.telephony.NetworkRegistrationInfo.Builder.setEmergencyOnly(boolean)
+
MissingGetterMatchingBuilder: android.telephony.ims.ImsSsData.Builder#setCallForwardingInfo(java.util.List<android.telephony.ims.ImsCallForwardInfo>):
- android.telephony.ims.ImsSsData does not declare a `getCallForwardingInfo()` method matching method android.telephony.ims.ImsSsData.Builder.setCallForwardingInfo(java.util.List<android.telephony.ims.ImsCallForwardInfo>)
+
MissingGetterMatchingBuilder: android.telephony.ims.stub.ImsFeatureConfiguration.Builder#addFeature(int, int):
- android.telephony.ims.stub.ImsFeatureConfiguration does not declare a `getFeatures()` method matching method android.telephony.ims.stub.ImsFeatureConfiguration.Builder.addFeature(int,int)
+
MissingGetterMatchingBuilder: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
- android.telephony.mbms.DownloadRequest does not declare a `getServiceId()` method matching method android.telephony.mbms.DownloadRequest.Builder.setServiceId(String)
+
MissingNullability: android.app.Activity#onMovedToDisplay(int, android.content.res.Configuration) parameter #1:
@@ -2590,7 +2602,7 @@
OnNameExpected: android.service.quicksettings.TileService#isQuickSettingsSupported():
OnNameExpected: android.service.watchdog.ExplicitHealthCheckService#setCallback(android.os.RemoteCallback):
- If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+
OnNameExpected: android.telephony.ims.ImsService#createMmTelFeature(int):
OnNameExpected: android.telephony.ims.ImsService#createRcsFeature(int):
@@ -2620,11 +2632,11 @@
OptionalBuilderConstructorArgument: android.app.prediction.AppTargetEvent.Builder#Builder(android.app.prediction.AppTarget, int) parameter #0:
- Builder constructor arguments must be mandatory (i.e. not @Nullable): parameter target in android.app.prediction.AppTargetEvent.Builder(android.app.prediction.AppTarget target, int actionType)
+
OptionalBuilderConstructorArgument: android.net.CaptivePortalData.Builder#Builder(android.net.CaptivePortalData) parameter #0:
- Builder constructor arguments must be mandatory (i.e. not @Nullable): parameter data in android.net.CaptivePortalData.Builder(android.net.CaptivePortalData data)
+
OptionalBuilderConstructorArgument: android.os.VibrationAttributes.Builder#Builder(android.media.AudioAttributes, android.os.VibrationEffect) parameter #1:
- Builder constructor arguments must be mandatory (i.e. not @Nullable): parameter effect in android.os.VibrationAttributes.Builder(android.media.AudioAttributes audio, android.os.VibrationEffect effect)
+
PackageLayering: android.util.FeatureFlagUtils:
@@ -2824,7 +2836,7 @@
SamShouldBeLast: android.os.StrictMode.ViolationInfo#dump(android.util.Printer, String):
SamShouldBeLast: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler):
- SAM-compatible parameters (such as parameter 3, "callback", in android.permission.PermissionControllerManager.countPermissionApps) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+
SamShouldBeLast: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler):
SamShouldBeLast: android.permission.PermissionControllerManager#revokeRuntimePermissions(java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, java.util.concurrent.Executor, android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback):
@@ -2870,21 +2882,21 @@
StaticFinalBuilder: android.content.integrity.RuleSet.Builder:
- Builder must be final: android.content.integrity.RuleSet.Builder
+
StaticFinalBuilder: android.hardware.display.BrightnessConfiguration.Builder:
- Builder must be final: android.hardware.display.BrightnessConfiguration.Builder
+
StaticFinalBuilder: android.media.audiopolicy.AudioMix.Builder:
- Builder must be final: android.media.audiopolicy.AudioMix.Builder
+
StaticFinalBuilder: android.media.audiopolicy.AudioMixingRule.Builder:
- Builder must be final: android.media.audiopolicy.AudioMixingRule.Builder
+
StaticFinalBuilder: android.media.audiopolicy.AudioPolicy.Builder:
- Builder must be final: android.media.audiopolicy.AudioPolicy.Builder
+
StaticFinalBuilder: android.net.CaptivePortalData.Builder:
- Builder must be final: android.net.CaptivePortalData.Builder
+
StaticFinalBuilder: android.net.TetheringManager.TetheringRequest.Builder:
- Builder must be final: android.net.TetheringManager.TetheringRequest.Builder
+
StaticFinalBuilder: android.telephony.ims.stub.ImsFeatureConfiguration.Builder:
- Builder must be final: android.telephony.ims.stub.ImsFeatureConfiguration.Builder
+
StaticUtils: android.os.health.HealthKeys:
@@ -2912,15 +2924,15 @@
UseIcu: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getKeyphraseMetadata(String, java.util.Locale) parameter #1:
- Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+
UseIcu: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getManageKeyphraseIntent(int, String, java.util.Locale) parameter #2:
- Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+
UseIcu: android.hardware.soundtrigger.KeyphraseMetadata#supportsLocale(java.util.Locale) parameter #0:
- Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+
UseIcu: android.hardware.soundtrigger.SoundTrigger.Keyphrase#Keyphrase(int, int, java.util.Locale, String, int[]) parameter #2:
- Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+
UseIcu: android.hardware.soundtrigger.SoundTrigger.Keyphrase#getLocale():
- Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+
UseParcelFileDescriptor: android.util.proto.ProtoOutputStream#ProtoOutputStream(java.io.FileDescriptor) parameter #0:
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index dec4a56..5c08704 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -30,8 +30,9 @@
#include <binder/ProcessState.h>
-#include <gui/SurfaceComposerClient.h>
#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SyncScreenCaptureListener.h>
#include <ui/DisplayInfo.h>
#include <ui/GraphicTypes.h>
@@ -181,13 +182,18 @@
ProcessState::self()->setThreadPoolMaxThreadCount(0);
ProcessState::self()->startThreadPool();
- ScreenCaptureResults captureResults;
- status_t result = ScreenshotClient::captureDisplay(displayId->value, captureResults);
+ sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+ status_t result = ScreenshotClient::captureDisplay(displayId->value, captureListener);
if (result != NO_ERROR) {
close(fd);
return 1;
}
+ ScreenCaptureResults captureResults = captureListener->waitForResults();
+ if (captureResults.result != NO_ERROR) {
+ close(fd);
+ return 1;
+ }
ui::Dataspace dataspace = captureResults.capturedDataspace;
sp<GraphicBuffer> buffer = captureResults.buffer;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 038f4c3..0a09801 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -38,6 +38,7 @@
import "frameworks/base/core/proto/android/os/enums.proto";
import "frameworks/base/core/proto/android/server/connectivity/data_stall_event.proto";
import "frameworks/base/core/proto/android/server/enums.proto";
+import "frameworks/base/core/proto/android/stats/hdmi/enums.proto";
import "frameworks/base/core/proto/android/server/job/enums.proto";
import "frameworks/base/core/proto/android/server/location/enums.proto";
import "frameworks/base/core/proto/android/service/procstats_enum.proto";
@@ -490,6 +491,8 @@
UIActionLatencyReported ui_action_latency_reported = 306 [(module) = "framework"];
WifiDisconnectReported wifi_disconnect_reported = 307 [(module) = "wifi"];
WifiConnectionStateChanged wifi_connection_state_changed = 308 [(module) = "wifi"];
+ HdmiCecActiveSourceChanged hdmi_cec_active_source_changed = 309 [(module) = "framework"];
+ HdmiCecMessageReported hdmi_cec_message_reported = 310 [(module) = "framework"];
// StatsdStats tracks platform atoms with ids upto 500.
// Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
@@ -4041,6 +4044,17 @@
* system/core/lmkd/lmkd.c
*/
message LmkKillOccurred {
+ enum Reason {
+ UNKNOWN = 0;
+ PRESSURE_AFTER_KILL = 1;
+ NOT_RESPONDING = 2;
+ LOW_SWAP_AND_THRASHING = 3;
+ LOW_MEM_AND_SWAP = 4;
+ LOW_MEM_AND_THRASHING = 5;
+ DIRECT_RECL_AND_THRASHING = 6;
+ LOW_MEM_AND_SWAP_UTIL = 7;
+ }
+
// The uid if available. -1 means not available.
optional int32 uid = 1 [(is_uid) = true];
@@ -4070,6 +4084,15 @@
// Min oom adj score considered by lmkd.
optional int32 min_oom_score = 10;
+
+ // Free physical memory on device at LMK time.
+ optional int32 free_mem_kb = 11;
+
+ // Free swap on device at LMK time.
+ optional int32 free_swap_kb = 12;
+
+ // What triggered the LMK event.
+ optional Reason reason = 13;
}
/*
@@ -11413,4 +11436,65 @@
// List of leasees of this Blob
optional BlobLeaseeListProto leasees = 5 [(log_mode) = MODE_BYTES];
-}
\ No newline at end of file
+}
+
+/**
+ * Logs when the HDMI CEC active source changes.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
+ */
+message HdmiCecActiveSourceChanged {
+ // The logical address of the active source.
+ optional android.stats.hdmi.LogicalAddress active_source_logical_address = 1;
+
+ // The physical address of the active source. Consists of four hexadecimal nibbles.
+ // Examples: 0x1234, 0x0000 (root device). 0xFFFF represents an unknown or invalid address.
+ // See section 8.7 in the HDMI 1.4b spec for details.
+ optional int32 active_source_physical_address = 2;
+
+ // The relationship between this device and the active source.
+ optional android.stats.hdmi.PathRelationship local_relationship = 3;
+}
+
+/**
+ * Logs when an HDMI CEC message is sent or received.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
+ */
+message HdmiCecMessageReported {
+ // The calling uid of the application that caused this atom to be written.
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Whether a HDMI CEC message is sent from this device, to this device, or neither.
+ optional android.stats.hdmi.MessageDirection direction = 2;
+
+ // The HDMI CEC logical address of the initiator.
+ optional android.stats.hdmi.LogicalAddress initiator_logical_address = 3;
+
+ // The HDMI CEC logical address of the destination.
+ optional android.stats.hdmi.LogicalAddress destination_logical_address = 4;
+
+ // The opcode of the message. Ranges from 0x00 to 0xFF.
+ // For all values, see section "CEC 15 Message Descriptions" in the HDMI CEC 1.4b spec.
+ optional int32 opcode = 5;
+
+ // The result of attempting to send the message on its final retransmission attempt.
+ // Only applicable to outgoing messages; set to SEND_MESSAGE_RESULT_UNKNOWN otherwise.
+ optional android.stats.hdmi.SendMessageResult send_message_result = 6;
+
+ // Fields specific to <User Control Pressed> messages
+
+ // The user control command that was received.
+ optional android.stats.hdmi.UserControlPressedCommand user_control_pressed_command = 7;
+
+ // Fields specific to <Feature Abort> messages
+
+ // The opcode of the message that was feature aborted.
+ // Set to 0x100 when unknown or not applicable.
+ optional int32 feature_abort_opcode = 8;
+
+ // The reason for the feature abort.
+ optional android.stats.hdmi.FeatureAbortReason feature_abort_reason = 9;
+}
diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
index 57c1083..4b4ef00 100644
--- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java
+++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
@@ -572,7 +572,7 @@
}
/**
- * Returns a set of whitelisted accounts given by the intent or null if none specified by the
+ * Returns a set of allowlisted accounts given by the intent or null if none specified by the
* intent.
*/
private Set<Account> getAllowableAccountSet(final Intent intent) {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 5c4951e..8c0b438 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -8341,7 +8341,7 @@
*
* <p>If {@link DevicePolicyManager#isLockTaskPermitted(String)} returns {@code true}
* for this component, the current task will be launched directly into LockTask mode. Only apps
- * whitelisted by {@link DevicePolicyManager#setLockTaskPackages(ComponentName, String[])} can
+ * allowlisted by {@link DevicePolicyManager#setLockTaskPackages(ComponentName, String[])} can
* be launched while LockTask mode is active. The user will not be able to leave this mode
* until this activity calls {@link #stopLockTask()}. Calling this method while the device is
* already in LockTask mode has no effect.
@@ -8373,7 +8373,7 @@
* <p><strong>Note:</strong> If the device is in LockTask mode that is not initially started
* by this activity, then calling this method will not terminate the LockTask mode, but only
* finish its own task. The device will remain in LockTask mode, until the activity which
- * started the LockTask mode calls this method, or until its whitelist authorization is revoked
+ * started the LockTask mode calls this method, or until its allowlist authorization is revoked
* by {@link DevicePolicyManager#setLockTaskPackages(ComponentName, String[])}.
*
* @see #startLockTask()
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 5aecb61..70ca49d 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4725,9 +4725,9 @@
}
/**
- * Get packages of bugreport-whitelisted apps to handle a bug report.
+ * Get packages of bugreport-allowlisted apps to handle a bug report.
*
- * @return packages of bugreport-whitelisted apps to handle a bug report.
+ * @return packages of bugreport-allowlisted apps to handle a bug report.
* @hide
*/
public List<String> getBugreportWhitelistedPackages() {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 763ce6c6..0fd0e02 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4074,7 +4074,7 @@
private void handleCreateBackupAgent(CreateBackupAgentData data) {
if (DEBUG_BACKUP) Slog.v(TAG, "handleCreateBackupAgent: " + data);
- // Sanity check the requested target package's uid against ours
+ // Validity check the requested target package's uid against ours
try {
PackageInfo requestedPackage = getPackageManager().getPackageInfo(
data.appInfo.packageName, 0, UserHandle.myUserId());
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 3cb6293..db95eae 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -85,16 +85,9 @@
}
public ActivityView(Context context, AttributeSet attrs) {
- this(context, attrs, 0 /* defStyle */);
- }
-
- public ActivityView(Context context, AttributeSet attrs, int defStyle) {
- this(context, attrs, defStyle, false /*singleTaskInstance*/);
- }
-
- public ActivityView(Context context, AttributeSet attrs, int defStyle,
- boolean singleTaskInstance) {
- this(context, attrs, defStyle, singleTaskInstance, false /* usePublicVirtualDisplay */);
+ this(context, attrs, 0 /* defStyle */, false /* singleTaskInstance */,
+ false /* usePublicVirtualDisplay */,
+ false /* disableSurfaceViewBackgroundLayer */, false /* useTrustedDisplay */);
}
/**
@@ -106,21 +99,11 @@
@NonNull Context context, @NonNull AttributeSet attrs, int defStyle,
boolean singleTaskInstance, boolean usePublicVirtualDisplay) {
this(context, attrs, defStyle, singleTaskInstance, usePublicVirtualDisplay,
- false /* disableSurfaceViewBackgroundLayer */);
+ false /* disableSurfaceViewBackgroundLayer */,
+ false /* useTrustedDisplay */);
}
- /** @hide */
- public ActivityView(
- @NonNull Context context, @NonNull AttributeSet attrs, int defStyle,
- boolean singleTaskInstance, boolean usePublicVirtualDisplay,
- boolean disableSurfaceViewBackgroundLayer) {
- this(context, attrs, defStyle, singleTaskInstance, usePublicVirtualDisplay,
- disableSurfaceViewBackgroundLayer, false /* useTrustedDisplay */);
- }
-
- // TODO(b/162901735): Refactor ActivityView with Builder
- /** @hide */
- public ActivityView(
+ private ActivityView(
@NonNull Context context, @NonNull AttributeSet attrs, int defStyle,
boolean singleTaskInstance, boolean usePublicVirtualDisplay,
boolean disableSurfaceViewBackgroundLayer, boolean useTrustedDisplay) {
@@ -133,7 +116,7 @@
}
mSurfaceView = new SurfaceView(context, null, 0, 0, disableSurfaceViewBackgroundLayer);
// Since ActivityView#getAlpha has been overridden, we should use parent class's alpha
- // as master to synchronize surface view's alpha value.
+ // as authoritative to synchronize surface view's alpha value.
mSurfaceView.setAlpha(super.getAlpha());
mSurfaceView.setUseAlpha();
mSurfaceCallback = new SurfaceCallback();
@@ -652,4 +635,97 @@
mCallback.onBackPressedOnTaskRoot(taskId);
}
}
+
+ /** The builder of {@link ActivityView} */
+ public static final class Builder {
+ private final Context mContext;
+ private AttributeSet mAttrs;
+ private int mDefStyle;
+ private boolean mSingleInstance;
+ private boolean mUsePublicVirtualDisplay;
+ private boolean mDisableSurfaceViewBackgroundLayer;
+ private boolean mUseTrustedDisplay;
+
+ public Builder(@NonNull Context context) {
+ mContext = context;
+ mAttrs = null;
+ mDefStyle = 0;
+ mSingleInstance = false;
+ mUsePublicVirtualDisplay = false;
+ mDisableSurfaceViewBackgroundLayer = false;
+ mUseTrustedDisplay = false;
+ }
+
+ /** Sets {@link AttributeSet} to the {@link ActivityView}. */
+ @NonNull
+ public Builder setAttributeSet(@Nullable AttributeSet attrs) {
+ mAttrs = attrs;
+ return this;
+ }
+
+ /**
+ * Sets {@code defStyle} to the {@link ActivityView}.
+ *
+ * @param defaultStyle An attribute in the current theme that contains a
+ * reference to a style resource that supplies default values for
+ * the view. Can be {@code 0} to not look for defaults.
+ */
+ @NonNull
+ public Builder setDefaultStyle(int defaultStyle) {
+ mDefStyle = defaultStyle;
+ return this;
+ }
+
+ /** Sets to {@code true} to make the {@link ActivityView} single instance. */
+ @NonNull
+ public Builder setSingleInstance(boolean singleInstance) {
+ mSingleInstance = singleInstance;
+ return this;
+ }
+
+ /**
+ * Sets to {@code true} to use public virtual display for the {@link ActivityView}.
+ * <p>
+ * Note that using a public display is not recommended as it exposes it to other
+ * applications, but it might be needed for backwards compatibility.
+ * </p>
+ */
+ @NonNull
+ public Builder setUsePublicVirtualDisplay(boolean usePublicVirtualDisplay) {
+ mUsePublicVirtualDisplay = usePublicVirtualDisplay;
+ return this;
+ }
+
+ /**
+ * Sets to {@code true} to disable {@link SurfaceView} background for the
+ * {@link ActivityView}.
+ */
+ @NonNull
+ public Builder setDisableSurfaceViewBackgroundLayer(
+ boolean disableSurfaceViewBackgroundLayer) {
+ mDisableSurfaceViewBackgroundLayer = disableSurfaceViewBackgroundLayer;
+ return this;
+ }
+
+ /**
+ * Sets to {@code true} to use trusted display for the {@link ActivityView}.
+ * <p>
+ * It enables the {@link ActivityView} to be focused without users' first touches.
+ * </p>
+ */
+ @NonNull
+ public Builder setUseTrustedDisplay(boolean useTrustedDisplay) {
+ mUseTrustedDisplay = useTrustedDisplay;
+ return this;
+ }
+
+ /** Creates an {@link ActivityView} */
+ @NonNull
+ public ActivityView build() {
+ return new ActivityView(mContext, mAttrs, mDefStyle, mSingleInstance,
+ mUsePublicVirtualDisplay, mDisableSurfaceViewBackgroundLayer,
+ mUseTrustedDisplay);
+ }
+ }
}
+
diff --git a/core/java/android/app/AppOps.md b/core/java/android/app/AppOps.md
index 536653e..b827a0d 100644
--- a/core/java/android/app/AppOps.md
+++ b/core/java/android/app/AppOps.md
@@ -53,7 +53,7 @@
: Allow the access but only if the app is currently in the [foreground](#foreground)
`MODE_IGNORED`
-: Don't allow the access, i.e. don't perform the requested action or return dummy data
+: Don't allow the access, i.e. don't perform the requested action or return placeholder data
`MODE_ERRORED`
: Throw a `SecurityException` on access. This can be suppressed by using a `...noThrow` method to
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 7087b60..167b5a8 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -115,8 +115,8 @@
* <dt>{@link #MODE_ALLOWED}
* <dd>Allow the access
* <dt>{@link #MODE_IGNORED}
- * <dd>Don't allow the access, i.e. don't perform the requested action or return no or dummy
- * data
+ * <dd>Don't allow the access, i.e. don't perform the requested action or return no or
+ * placeholder data
* <dt>{@link #MODE_ERRORED}
* <dd>Throw a {@link SecurityException} on access. This can be suppressed by using a
* {@code ...noThrow} method to check the mode
@@ -135,7 +135,7 @@
* <p>Each platform defined runtime permission (beside background modifiers) has an associated app
* op which is used for tracking but also to allow for silent failures. I.e. if the runtime
* permission is denied the caller gets a {@link SecurityException}, but if the permission is
- * granted and the app-op is {@link #MODE_IGNORED} then the callers gets dummy behavior, e.g.
+ * granted and the app-op is {@link #MODE_IGNORED} then the callers gets placeholder behavior, e.g.
* location callbacks would not happen.
*
* <h3>App-op permissions</h3>
@@ -1964,7 +1964,7 @@
null, // no permission for writing clipboard
null, // no permission for taking media buttons
null, // no permission for taking audio focus
- null, // no permission for changing master volume
+ null, // no permission for changing global volume
null, // no permission for changing voice volume
null, // no permission for changing ring volume
null, // no permission for changing media volume
@@ -6525,7 +6525,7 @@
* Retrieve current operation state for all applications.
*
* The mode of the ops returned are set for the package but may not reflect their effective
- * state due to UID policy or because it's controlled by a different master op.
+ * state due to UID policy or because it's controlled by a different global op.
*
* Use {@link #unsafeCheckOp(String, int, String)}} or
* {@link #noteOp(String, int, String, String, String)} if the effective mode is needed.
@@ -6549,7 +6549,7 @@
* Retrieve current operation state for all applications.
*
* The mode of the ops returned are set for the package but may not reflect their effective
- * state due to UID policy or because it's controlled by a different master op.
+ * state due to UID policy or because it's controlled by a different global op.
*
* Use {@link #unsafeCheckOp(String, int, String)}} or
* {@link #noteOp(String, int, String, String, String)} if the effective mode is needed.
@@ -6571,7 +6571,7 @@
* Retrieve current operation state for one application.
*
* The mode of the ops returned are set for the package but may not reflect their effective
- * state due to UID policy or because it's controlled by a different master op.
+ * state due to UID policy or because it's controlled by a different global op.
*
* Use {@link #unsafeCheckOp(String, int, String)}} or
* {@link #noteOp(String, int, String, String, String)} if the effective mode is needed.
@@ -6604,7 +6604,7 @@
* package must match.
*
* The mode of the ops returned are set for the package but may not reflect their effective
- * state due to UID policy or because it's controlled by a different master op.
+ * state due to UID policy or because it's controlled by a different global op.
*
* Use {@link #unsafeCheckOp(String, int, String)}} or
* {@link #noteOp(String, int, String, String, String)} if the effective mode is needed.
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 066a007..3d966c7 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -471,6 +471,19 @@
}
@Override
+ public int getTargetSdkVersion(@NonNull String packageName) throws NameNotFoundException {
+ try {
+ int version = mPM.getTargetSdkVersion(packageName);
+ if (version != -1) {
+ return version;
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ throw new PackageManager.NameNotFoundException(packageName);
+ }
+
+ @Override
public ActivityInfo getActivityInfo(ComponentName className, int flags)
throws NameNotFoundException {
final int userId = getUserId();
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 161e2ad..3789a44 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -36,7 +36,7 @@
private boolean mAllowBackgroundActivityStarts;
/**
- * How long to temporarily put an app on the power whitelist when executing this broadcast
+ * How long to temporarily put an app on the power allowlist when executing this broadcast
* to it.
*/
static final String KEY_TEMPORARY_APP_WHITELIST_DURATION
@@ -87,8 +87,8 @@
/**
* Set a duration for which the system should temporary place an application on the
- * power whitelist when this broadcast is being delivered to it.
- * @param duration The duration in milliseconds; 0 means to not place on whitelist.
+ * power allowlist when this broadcast is being delivered to it.
+ * @param duration The duration in milliseconds; 0 means to not place on allowlist.
*/
@RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
public void setTemporaryAppWhitelistDuration(long duration) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 1ff48f6..0d682d6 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -953,7 +953,7 @@
public ArraySet<PendingIntent> allPendingIntents;
/**
- * Token identifying the notification that is applying doze/bgcheck whitelisting to the
+ * Token identifying the notification that is applying doze/bgcheck allowlisting to the
* pending intents inside of it, so only those will get the behavior.
*
* @hide
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 0627bc8..fe89366 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -131,6 +132,73 @@
"android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED";
/**
+ * Activity action: Toggle notification panel of the specified handler.
+ *
+ * <p><strong>Important:</strong>You must protect the activity that handles this action with
+ * the {@link android.Manifest.permission#STATUS_BAR_SERVICE} permission to ensure that only
+ * the SystemUI can launch this activity. Activities that are not properly protected will not
+ * be launched.
+ *
+ * <p class="note">This is currently only used on TV to allow a system app to handle the
+ * notification panel. The package handling the notification panel has to be specified by
+ * config_notificationHandlerPackage in values/config.xml.
+ *
+ * Input: nothing
+ * Output: nothing
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL =
+ "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL";
+
+ /**
+ * Activity action: Open notification panel of the specified handler.
+ *
+ * <p><strong>Important:</strong>You must protect the activity that handles this action with
+ * the {@link android.Manifest.permission#STATUS_BAR_SERVICE} permission to ensure that only
+ * the SystemUI can launch this activity. Activities that are not properly protected will
+ * not be launched.
+ *
+ * <p class="note"> This is currently only used on TV to allow a system app to handle the
+ * notification panel. The package handling the notification panel has to be specified by
+ * config_notificationHandlerPackage in values/config.xml.
+ *
+ * Input: nothing
+ * Output: nothing
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL =
+ "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL";
+
+ /**
+ * Intent that is broadcast when the notification panel of the specified handler is to be
+ * closed.
+ *
+ * <p><strong>Important:</strong>You should protect the receiver that handles this action with
+ * the {@link android.Manifest.permission#STATUS_BAR_SERVICE} permission to ensure that only
+ * the SystemUI can send this broadcast to the notification handler.
+ *
+ * <p class="note"> This is currently only used on TV to allow a system app to handle the
+ * notification panel. The package handling the notification panel has to be specified by
+ * config_notificationHandlerPackage in values/config.xml. This is a protected intent that can
+ * only be sent by the system.
+ *
+ * Input: nothing.
+ * Output: nothing.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL =
+ "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL";
+
+ /**
* Extra for {@link #ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED} containing the id of the
* {@link NotificationChannel} which has a new blocked state.
*
diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java
index 18c4d70..df53f98 100644
--- a/core/java/android/app/PictureInPictureParams.java
+++ b/core/java/android/app/PictureInPictureParams.java
@@ -47,7 +47,7 @@
@Nullable
private Rect mSourceRectHint;
- private boolean mAutoEnterAllowed;
+ private boolean mAutoEnterEnabled;
/**
* Sets the aspect ratio. This aspect ratio is defined as the desired width / height, and
@@ -106,21 +106,21 @@
}
/**
- * Sets whether the system is allowed to automatically put the activity in
- * picture-in-picture mode without needing/waiting for the activity to call
+ * Sets whether the system will automatically put the activity in picture-in-picture mode
+ * without needing/waiting for the activity to call
* {@link Activity#enterPictureInPictureMode(PictureInPictureParams)}.
*
* If true, {@link Activity#onPictureInPictureRequested()} will never be called.
*
* This property is false by default.
- * @param autoEnterAllowed {@code true} if the system is allowed to automatically put the
- * activity in picture-in-picture mode.
+ * @param autoEnterEnabled {@code true} if the system will automatically put the activity
+ * in picture-in-picture mode.
*
* @return this builder instance.
*/
@NonNull
- public Builder setAutoEnterAllowed(boolean autoEnterAllowed) {
- mAutoEnterAllowed = autoEnterAllowed;
+ public Builder setAutoEnterEnabled(boolean autoEnterEnabled) {
+ mAutoEnterEnabled = autoEnterEnabled;
return this;
}
@@ -133,7 +133,7 @@
*/
public PictureInPictureParams build() {
PictureInPictureParams params = new PictureInPictureParams(mAspectRatio, mUserActions,
- mSourceRectHint, mAutoEnterAllowed);
+ mSourceRectHint, mAutoEnterEnabled);
return params;
}
}
@@ -161,7 +161,7 @@
/**
* Whether the system is allowed to automatically put the activity in picture-in-picture mode.
*/
- private boolean mAutoEnterAllowed;
+ private boolean mAutoEnterEnabled;
/** {@hide} */
PictureInPictureParams() {
@@ -180,17 +180,17 @@
mSourceRectHint = Rect.CREATOR.createFromParcel(in);
}
if (in.readInt() != 0) {
- mAutoEnterAllowed = in.readBoolean();
+ mAutoEnterEnabled = in.readBoolean();
}
}
/** {@hide} */
PictureInPictureParams(Rational aspectRatio, List<RemoteAction> actions,
- Rect sourceRectHint, boolean autoEnterAllowed) {
+ Rect sourceRectHint, boolean autoEnterEnabled) {
mAspectRatio = aspectRatio;
mUserActions = actions;
mSourceRectHint = sourceRectHint;
- mAutoEnterAllowed = autoEnterAllowed;
+ mAutoEnterEnabled = autoEnterEnabled;
}
/**
@@ -207,7 +207,7 @@
if (otherArgs.hasSourceBoundsHint()) {
mSourceRectHint = new Rect(otherArgs.getSourceRectHint());
}
- mAutoEnterAllowed = otherArgs.mAutoEnterAllowed;
+ mAutoEnterEnabled = otherArgs.mAutoEnterEnabled;
}
/**
@@ -280,11 +280,11 @@
}
/**
- * @return whether auto pip allowed.
+ * @return whether auto pip is enabled.
* @hide
*/
- public boolean isAutoEnterAllowed() {
- return mAutoEnterAllowed;
+ public boolean isAutoEnterEnabled() {
+ return mAutoEnterEnabled;
}
/**
@@ -293,7 +293,7 @@
*/
public boolean empty() {
return !hasSourceBoundsHint() && !hasSetActions() && !hasSetAspectRatio()
- && !mAutoEnterAllowed;
+ && !mAutoEnterEnabled;
}
@Override
@@ -323,7 +323,7 @@
out.writeInt(0);
}
out.writeInt(1);
- out.writeBoolean(mAutoEnterAllowed);
+ out.writeBoolean(mAutoEnterEnabled);
}
public static final @android.annotation.NonNull Creator<PictureInPictureParams> CREATOR =
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 6c6c04e..3e3a956 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -168,8 +168,8 @@
* this local case, there's no IPC, so use of the cache is (depending on exact
* circumstance) unnecessary.
*
- * For security, there is a whitelist of processes that are allowed to invalidate a cache.
- * The whitelist includes normal runtime processes but does not include test processes.
+ * For security, there is a allowlist of processes that are allowed to invalidate a cache.
+ * The allowlist includes normal runtime processes but does not include test processes.
* Test processes must call {@code PropertyInvalidatedCache.disableForTestMode()} to disable
* all cache activity in that process.
*
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 95f55ab..dcb5350 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -521,7 +521,7 @@
/**
* This means that context is voice, and therefore the SearchDialog should
- * continue showing the microphone until the user indicates that he/she does
+ * continue showing the microphone until the user indicates that they do
* not want to re-speak (e.g. by typing).
*
* @hide
diff --git a/core/java/android/app/SearchableInfo.java b/core/java/android/app/SearchableInfo.java
index 83eb2ee..5388282 100644
--- a/core/java/android/app/SearchableInfo.java
+++ b/core/java/android/app/SearchableInfo.java
@@ -418,7 +418,7 @@
com.android.internal.R.styleable.SearchableActionKey_suggestActionMsgColumn);
a.recycle();
- // sanity check.
+ // validity check.
if (mKeyCode == 0) {
throw new IllegalArgumentException("No keycode.");
} else if ((mQueryActionMsg == null) &&
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 1329fa4..4ff9e8d 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -400,6 +400,7 @@
* @hide
*/
@TestApi
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@RequiresPermission(android.Manifest.permission.STATUS_BAR)
public void setDisabledForSimNetworkLock(boolean disabled) {
try {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 3b11b0d..d50cdee 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1126,7 +1126,7 @@
// Get the services without throwing as this is an optional feature
Context outerContext = ctx.getOuterContext();
ContentCaptureOptions options = outerContext.getContentCaptureOptions();
- // Options is null when the service didn't whitelist the activity or package
+ // Options is null when the service didn't allowlist the activity or package
if (options != null && (options.lite || options.isWhitelisted(outerContext))) {
IBinder b = ServiceManager
.getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE);
@@ -1136,7 +1136,7 @@
return new ContentCaptureManager(outerContext, service, options);
}
}
- // When feature is disabled or app / package not whitelisted, we return a null
+ // When feature is disabled or app / package not allowlisted, we return a null
// manager to apps so the performance impact is practically zero
return null;
}});
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 4139b2f..b0307d1 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -70,7 +70,7 @@
"name": "CtsAutoFillServiceTestCases",
"options": [
{
- "include-filter": "android.autofillservice.cts.PreSimpleSaveActivityTest"
+ "include-filter": "android.autofillservice.cts.saveui.PreSimpleSaveActivityTest"
},
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
@@ -82,7 +82,7 @@
"name": "CtsAutoFillServiceTestCases",
"options": [
{
- "include-filter": "android.autofillservice.cts.SimpleSaveActivityTest"
+ "include-filter": "android.autofillservice.cts.saveui.SimpleSaveActivityTest"
},
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index e4818b2..7624f35 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -74,7 +74,7 @@
*/
public static final int HINT_FROM_BITMAP = 1 << 2;
- // Maximum size that a bitmap can have to keep our calculations sane
+ // Maximum size that a bitmap can have to keep our calculations valid
private static final int MAX_BITMAP_SIZE = 112;
// Even though we have a maximum size, we'll mainly match bitmap sizes
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7b3d9f1..8a85b79 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2051,7 +2051,7 @@
* Enable the Home button during LockTask mode. Note that if a custom launcher is used, it has
* to be registered as the default launcher with
* {@link #addPersistentPreferredActivity(ComponentName, IntentFilter, ComponentName)}, and its
- * package needs to be whitelisted for LockTask with
+ * package needs to be allowlisted for LockTask with
* {@link #setLockTaskPackages(ComponentName, String[])}.
*
* @see #setLockTaskFeatures(ComponentName, int)
@@ -2092,7 +2092,7 @@
public static final int LOCK_TASK_FEATURE_KEYGUARD = 1 << 5;
/**
- * Enable blocking of non-whitelisted activities from being started into a locked task.
+ * Enable blocking of non-allowlisted activities from being started into a locked task.
*
* @see #setLockTaskFeatures(ComponentName, int)
*/
@@ -4713,7 +4713,7 @@
/**
* Disable trust agents on secure keyguard screens (e.g. PIN/Pattern/Password).
* By setting this flag alone, all trust agents are disabled. If the admin then wants to
- * whitelist specific features of some trust agent, {@link #setTrustAgentConfiguration} can be
+ * allowlist specific features of some trust agent, {@link #setTrustAgentConfiguration} can be
* used in conjuction to set trust-agent-specific configurations.
*/
public static final int KEYGUARD_DISABLE_TRUST_AGENTS = 1 << 4;
@@ -5694,7 +5694,7 @@
* The call will fail if called with the package name of an unsupported VPN app.
* <p> Enabling lockdown via {@code lockdownEnabled} argument carries the risk that any failure
* of the VPN provider could break networking for all apps. This method clears any lockdown
- * whitelist set by {@link #setAlwaysOnVpnPackage(ComponentName, String, boolean, Set)}.
+ * allowlist set by {@link #setAlwaysOnVpnPackage(ComponentName, String, boolean, Set)}.
*
* @param vpnPackage The package name for an installed VPN app on the device, or {@code null} to
* remove an existing always-on VPN configuration.
@@ -5716,13 +5716,13 @@
* admin to specify a set of apps that should be able to access the network directly when VPN
* is not connected. When VPN connects these apps switch over to VPN if allowed to use that VPN.
* System apps can always bypass VPN.
- * <p> Note that the system doesn't update the whitelist when packages are installed or
+ * <p> Note that the system doesn't update the allowlist when packages are installed or
* uninstalled, the admin app must call this method to keep the list up to date.
* <p> When {@code lockdownEnabled} is false {@code lockdownWhitelist} is ignored . When
* {@code lockdownEnabled} is {@code true} and {@code lockdownWhitelist} is {@code null} or
* empty, only system apps can bypass VPN.
* <p> Setting always-on VPN package to {@code null} or using
- * {@link #setAlwaysOnVpnPackage(ComponentName, String, boolean)} clears lockdown whitelist.
+ * {@link #setAlwaysOnVpnPackage(ComponentName, String, boolean)} clears lockdown allowlist.
*
* @param vpnPackage package name for an installed VPN app on the device, or {@code null}
* to remove an existing always-on VPN configuration
@@ -7955,7 +7955,7 @@
* {@link #setApplicationHidden(ComponentName, String, boolean)})
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param packageList List of package names to whitelist
+ * @param packageList List of package names to allowlist
* @return true if setting the restriction succeeded. It will fail if called outside a managed
* profile
* @throws SecurityException if {@code admin} is not a profile owner.
@@ -9056,7 +9056,7 @@
}
/**
- * Called by device owners to set the user's master location setting.
+ * Called by device owners to set the user's global location setting.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with
* @param locationEnabled whether location should be enabled or disabled
@@ -9155,11 +9155,11 @@
}
/**
- * Called by profile or device owners to set the master volume mute on or off.
+ * Called by profile or device owners to set the global volume mute on or off.
* This has no effect when set on a managed profile.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param on {@code true} to mute master volume, {@code false} to turn mute off.
+ * @param on {@code true} to mute global volume, {@code false} to turn mute off.
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public void setMasterVolumeMuted(@NonNull ComponentName admin, boolean on) {
@@ -9174,10 +9174,10 @@
}
/**
- * Called by profile or device owners to check whether the master volume mute is on or off.
+ * Called by profile or device owners to check whether the global volume mute is on or off.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @return {@code true} if master volume is muted, {@code false} if it's not.
+ * @return {@code true} if global volume is muted, {@code false} if it's not.
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public boolean isMasterVolumeMuted(@NonNull ComponentName admin) {
@@ -9248,14 +9248,14 @@
/**
* Called by the profile owner of a managed profile to enable widget providers from a given
* package to be available in the parent profile. As a result the user will be able to add
- * widgets from the white-listed package running under the profile to a widget host which runs
+ * widgets from the allowlisted package running under the profile to a widget host which runs
* under the parent profile, for example the home screen. Note that a package may have zero or
* more provider components, where each component provides a different widget type.
* <p>
- * <strong>Note:</strong> By default no widget provider package is white-listed.
+ * <strong>Note:</strong> By default no widget provider package is allowlisted.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param packageName The package from which widget providers are white-listed.
+ * @param packageName The package from which widget providers are allowlisted.
* @return Whether the package was added.
* @throws SecurityException if {@code admin} is not a profile owner.
* @see #removeCrossProfileWidgetProvider(android.content.ComponentName, String)
@@ -9279,10 +9279,10 @@
* should have been added via
* {@link #addCrossProfileWidgetProvider( android.content.ComponentName, String)}.
* <p>
- * <strong>Note:</strong> By default no widget provider package is white-listed.
+ * <strong>Note:</strong> By default no widget provider package is allowlisted.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param packageName The package from which widget providers are no longer white-listed.
+ * @param packageName The package from which widget providers are no longer allowlisted.
* @return Whether the package was removed.
* @throws SecurityException if {@code admin} is not a profile owner.
* @see #addCrossProfileWidgetProvider(android.content.ComponentName, String)
@@ -9306,7 +9306,7 @@
* available in the parent profile.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @return The white-listed package list.
+ * @return The allowlisted package list.
* @see #addCrossProfileWidgetProvider(android.content.ComponentName, String)
* @see #removeCrossProfileWidgetProvider(android.content.ComponentName, String)
* @throws SecurityException if {@code admin} is not a profile owner.
@@ -11627,7 +11627,7 @@
* called, no package is allowed to access cross-profile calendar APIs by default.
*
* @param admin which {@link DeviceAdminReceiver} this request is associated with
- * @param packageNames set of packages to be whitelisted
+ * @param packageNames set of packages to be allowlisted
* @throws SecurityException if {@code admin} is not a profile owner
*
* @see #getCrossProfileCalendarPackages(ComponentName)
@@ -11734,7 +11734,7 @@
}
/**
- * Sets the set of admin-whitelisted package names that are allowed to request user consent for
+ * Sets the set of admin-allowlisted package names that are allowed to request user consent for
* cross-profile communication.
*
* <p>Assumes that the caller is a profile owner and is the given {@code admin}.
@@ -11742,11 +11742,11 @@
* <p>Previous calls are overridden by each subsequent call to this method.
*
* <p>Note that other apps may be able to request user consent for cross-profile communication
- * if they have been explicitly whitelisted by the OEM.
+ * if they have been explicitly allowlisted by the OEM.
*
* <p>When previously-set cross-profile packages are missing from {@code packageNames}, the
* app-op for {@code INTERACT_ACROSS_PROFILES} will be reset for those packages. This will not
- * occur for packages that are whitelisted by the OEM.
+ * occur for packages that are allowlisted by the OEM.
*
* @param admin the {@link DeviceAdminReceiver} this request is associated with
* @param packageNames the new cross-profile package names
@@ -11771,7 +11771,7 @@
* <p>Assumes that the caller is a profile owner and is the given {@code admin}.
*
* <p>Note that other apps not included in the returned set may be able to request user consent
- * for cross-profile communication if they have been explicitly whitelisted by the OEM.
+ * for cross-profile communication if they have been explicitly allowlisted by the OEM.
*
* @param admin the {@link DeviceAdminReceiver} this request is associated with
* @return the set of package names the admin has previously set as allowed to request user
@@ -11802,7 +11802,7 @@
* #vendor_cross_profile_apps}.</li>
* </ul>
*
- * @return the combined set of whitelisted package names set via
+ * @return the combined set of allowlisted package names set via
* {@link #setCrossProfilePackages(ComponentName, Set)}, {@link com.android.internal.R.array
* #cross_profile_apps}, and {@link com.android.internal.R.array#vendor_cross_profile_apps}.
*
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 8f5dbc4..19242ba 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -36,36 +36,36 @@
public abstract class DevicePolicyManagerInternal {
/**
- * Listener for changes in the white-listed packages to show cross-profile
+ * Listener for changes in the allowlisted packages to show cross-profile
* widgets.
*/
public interface OnCrossProfileWidgetProvidersChangeListener {
/**
- * Called when the white-listed packages to show cross-profile widgets
+ * Called when the allowlisted packages to show cross-profile widgets
* have changed for a given user.
*
- * @param profileId The profile for which the white-listed packages changed.
- * @param packages The white-listed packages.
+ * @param profileId The profile for which the allowlisted packages changed.
+ * @param packages The allowlisted packages.
*/
public void onCrossProfileWidgetProvidersChanged(int profileId, List<String> packages);
}
/**
- * Gets the packages whose widget providers are white-listed to be
+ * Gets the packages whose widget providers are allowlisted to be
* available in the parent user.
*
* <p>This takes the DPMS lock. DO NOT call from PM/UM/AM with their lock held.
*
* @param profileId The profile id.
* @return The list of packages if such or empty list if there are
- * no white-listed packages or the profile id is not a managed
+ * no allowlisted packages or the profile id is not a managed
* profile.
*/
public abstract List<String> getCrossProfileWidgetProviders(int profileId);
/**
- * Adds a listener for changes in the white-listed packages to show
+ * Adds a listener for changes in the allowlisted packages to show
* cross-profile app widgets.
*
* <p>This takes the DPMS lock. DO NOT call from PM/UM/AM with their lock held.
@@ -181,7 +181,7 @@
* {@link com.android.internal.R.array#vendor_cross_profile_apps}.</li>
* </ul>
*
- * @return the combined set of whitelisted package names set via
+ * @return the combined set of allowlisted package names set via
* {@link DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)} and
* {@link com.android.internal.R.array#cross_profile_apps} and
* {@link com.android.internal.R.array#vendor_cross_profile_apps}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 1345e66..f74d16e 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -1186,13 +1186,13 @@
/**
* {@hide}
- * Temporarily whitelist the specified app for a short duration. This is to allow an app
+ * Temporarily allowlist the specified app for a short duration. This is to allow an app
* receiving a high priority message to be able to access the network and acquire wakelocks
* even if the device is in power-save mode or the app is currently considered inactive.
- * @param packageName The package name of the app to whitelist.
- * @param duration Duration to whitelist the app for, in milliseconds. It is recommended that
+ * @param packageName The package name of the app to allowlist.
+ * @param duration Duration to allowlist the app for, in milliseconds. It is recommended that
* this be limited to 10s of seconds. Requested duration will be clamped to a few minutes.
- * @param user The user for whom the package should be whitelisted. Passing in a user that is
+ * @param user The user for whom the package should be allowlisted. Passing in a user that is
* not the same as the caller's process will require the INTERACT_ACROSS_USERS permission.
* @see #isAppInactive(String)
*
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index c7b20b2..6a68796 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2504,7 +2504,7 @@
* {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number.
*
* @param channel RFCOMM channel to listen on
- * @param mitm enforce man-in-the-middle protection for authentication.
+ * @param mitm enforce person-in-the-middle protection for authentication.
* @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2
* connections.
* @return a listening RFCOMM BluetoothServerSocket
@@ -2562,7 +2562,7 @@
/**
* Create a listening, insecure RFCOMM Bluetooth socket with Service Record.
* <p>The link key is not required to be authenticated, i.e the communication may be
- * vulnerable to Man In the Middle attacks. For Bluetooth 2.1 devices,
+ * vulnerable to Person In the Middle attacks. For Bluetooth 2.1 devices,
* the link will be encrypted, as encryption is mandartory.
* For legacy devices (pre Bluetooth 2.1 devices) the link will not
* be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an
@@ -2595,7 +2595,7 @@
* Create a listening, encrypted,
* RFCOMM Bluetooth socket with Service Record.
* <p>The link will be encrypted, but the link key is not required to be authenticated
- * i.e the communication is vulnerable to Man In the Middle attacks. Use
+ * i.e the communication is vulnerable to Person In the Middle attacks. Use
* {@link #listenUsingRfcommWithServiceRecord}, to ensure an authenticated link key.
* <p> Use this socket if authentication of link key is not possible.
* For example, for Bluetooth 2.1 devices, if any of the devices does not have
@@ -2680,7 +2680,7 @@
* {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
*
* @param port the PSM to listen on
- * @param mitm enforce man-in-the-middle protection for authentication.
+ * @param mitm enforce person-in-the-middle protection for authentication.
* @param min16DigitPin enforce a pin key length og minimum 16 digit for sec mode 2
* connections.
* @return An L2CAP BluetoothServerSocket
@@ -3381,7 +3381,7 @@
* assign a dynamic PSM value. This socket can be used to listen for incoming connections. The
* supported Bluetooth transport is LE only.
* <p>The link key is not required to be authenticated, i.e the communication may be vulnerable
- * to man-in-the-middle attacks. Use {@link #listenUsingL2capChannel}, if an encrypted and
+ * to person-in-the-middle attacks. Use {@link #listenUsingL2capChannel}, if an encrypted and
* authenticated communication channel is desired.
* <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
* {@link BluetoothServerSocket}.
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index fd60560..6287453 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1845,7 +1845,7 @@
* socket will be encrypted.
* <p> Use this socket only if an authenticated socket link is possible.
* Authentication refers to the authentication of the link key to
- * prevent man-in-the-middle type of attacks.
+ * prevent person-in-the-middle type of attacks.
* For example, for Bluetooth 2.1 devices, if any of the devices does not
* have an input and output capability or just has the ability to
* display a numeric key, a secure socket connection is not possible.
@@ -1880,7 +1880,7 @@
* socket will be encrypted.
* <p> Use this socket only if an authenticated socket link is possible.
* Authentication refers to the authentication of the link key to
- * prevent man-in-the-middle type of attacks.
+ * prevent person-in-the-middle type of attacks.
* For example, for Bluetooth 2.1 devices, if any of the devices does not
* have an input and output capability or just has the ability to
* display a numeric key, a secure socket connection is not possible.
@@ -1937,7 +1937,7 @@
* socket will be encrypted.
* <p> Use this socket only if an authenticated socket link is possible.
* Authentication refers to the authentication of the link key to
- * prevent man-in-the-middle type of attacks.
+ * prevent person-in-the-middle type of attacks.
* For example, for Bluetooth 2.1 devices, if any of the devices does not
* have an input and output capability or just has the ability to
* display a numeric key, a secure socket connection is not possible.
@@ -1969,7 +1969,7 @@
* Create an RFCOMM {@link BluetoothSocket} socket ready to start an insecure
* outgoing connection to this remote device using SDP lookup of uuid.
* <p> The communication channel will not have an authenticated link key
- * i.e it will be subject to man-in-the-middle attacks. For Bluetooth 2.1
+ * i.e it will be subject to person-in-the-middle attacks. For Bluetooth 2.1
* devices, the link key will be encrypted, as encryption is mandatory.
* For legacy devices (pre Bluetooth 2.1 devices) the link key will
* be not be encrypted. Use {@link #createRfcommSocketToServiceRecord} if an
@@ -2225,7 +2225,7 @@
* <p>The remote device will be authenticated and communication on this socket will be
* encrypted.
* <p> Use this socket if an authenticated socket link is possible. Authentication refers
- * to the authentication of the link key to prevent man-in-the-middle type of attacks.
+ * to the authentication of the link key to prevent person-in-the-middle type of attacks.
*
* @param psm dynamic PSM value from remote device
* @return a CoC #BluetoothSocket ready for an outgoing connection
@@ -2252,7 +2252,7 @@
* <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
* <p>Application using this API is responsible for obtaining PSM value from remote device.
* <p> The communication channel may not have an authenticated link key, i.e. it may be subject
- * to man-in-the-middle attacks. Use {@link #createL2capChannel(int)} if an encrypted and
+ * to person-in-the-middle attacks. Use {@link #createL2capChannel(int)} if an encrypted and
* authenticated communication channel is possible.
*
* @param psm dynamic PSM value from remote device
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index f877f04..c58b5d2 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -134,14 +134,14 @@
/*package*/ static final int AUTHENTICATION_NONE = 0;
/**
- * Authentication requested; no man-in-the-middle protection required.
+ * Authentication requested; no person-in-the-middle protection required.
*
* @hide
*/
/*package*/ static final int AUTHENTICATION_NO_MITM = 1;
/**
- * Authentication with man-in-the-middle protection requested.
+ * Authentication with person-in-the-middle protection requested.
*
* @hide
*/
diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java
index cf82a33..f718c0b 100644
--- a/core/java/android/bluetooth/BluetoothGattCallback.java
+++ b/core/java/android/bluetooth/BluetoothGattCallback.java
@@ -183,7 +183,7 @@
* @param gatt GATT client involved
* @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from
* 6 (7.5ms) to 3200 (4000ms).
- * @param latency Slave latency for the connection in number of connection events. Valid range
+ * @param latency Worker latency for the connection in number of connection events. Valid range
* is from 0 to 499
* @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is from 10
* (0.1s) to 3200 (32s)
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
index 7066f47..8f1b59c 100644
--- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java
+++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
@@ -84,7 +84,7 @@
public static final int PERMISSION_READ_ENCRYPTED = 0x02;
/**
- * Characteristic permission: Allow reading with man-in-the-middle protection
+ * Characteristic permission: Allow reading with person-in-the-middle protection
*/
public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04;
@@ -99,7 +99,7 @@
public static final int PERMISSION_WRITE_ENCRYPTED = 0x20;
/**
- * Characteristic permission: Allow encrypted writes with man-in-the-middle
+ * Characteristic permission: Allow encrypted writes with person-in-the-middle
* protection
*/
public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40;
@@ -111,7 +111,7 @@
/**
* Characteristic permission: Allow signed write operations with
- * man-in-the-middle protection
+ * person-in-the-middle protection
*/
public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100;
diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java
index 7cc2d6b..49ba281 100644
--- a/core/java/android/bluetooth/BluetoothGattDescriptor.java
+++ b/core/java/android/bluetooth/BluetoothGattDescriptor.java
@@ -58,7 +58,7 @@
public static final int PERMISSION_READ_ENCRYPTED = 0x02;
/**
- * Descriptor permission: Allow reading with man-in-the-middle protection
+ * Descriptor permission: Allow reading with person-in-the-middle protection
*/
public static final int PERMISSION_READ_ENCRYPTED_MITM = 0x04;
@@ -73,7 +73,7 @@
public static final int PERMISSION_WRITE_ENCRYPTED = 0x20;
/**
- * Descriptor permission: Allow encrypted writes with man-in-the-middle
+ * Descriptor permission: Allow encrypted writes with person-in-the-middle
* protection
*/
public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 0x40;
@@ -85,7 +85,7 @@
/**
* Descriptor permission: Allow signed write operations with
- * man-in-the-middle protection
+ * person-in-the-middle protection
*/
public static final int PERMISSION_WRITE_SIGNED_MITM = 0x100;
diff --git a/core/java/android/bluetooth/BluetoothGattServerCallback.java b/core/java/android/bluetooth/BluetoothGattServerCallback.java
index 2c8114b..0ead5f5 100644
--- a/core/java/android/bluetooth/BluetoothGattServerCallback.java
+++ b/core/java/android/bluetooth/BluetoothGattServerCallback.java
@@ -187,7 +187,7 @@
* @param device The remote device involved
* @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from
* 6 (7.5ms) to 3200 (4000ms).
- * @param latency Slave latency for the connection in number of connection events. Valid range
+ * @param latency Worker latency for the connection in number of connection events. Valid range
* is from 0 to 499
* @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is from 10
* (0.1s) to 3200 (32s)
diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java
index 13d6d70..e7809ae 100644
--- a/core/java/android/bluetooth/BluetoothGattService.java
+++ b/core/java/android/bluetooth/BluetoothGattService.java
@@ -44,7 +44,7 @@
/**
- * The remote device his service is associated with.
+ * The remote device this service is associated with.
* This applies to client applications only.
*
* @hide
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 73c38cb..ce3c7d2 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -89,6 +89,33 @@
@SuppressLint("ActionValue")
public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE";
+ /**
+ * Intent used to broadcast the change in tethering state of the Pan
+ * Profile
+ *
+ * <p>This intent will have 1 extra:
+ * <ul>
+ * <li> {@link #EXTRA_TETHERING_STATE} - The current state of Bluetooth
+ * tethering. </li>
+ * </ul>
+ *
+ * <p> {@link #EXTRA_TETHERING_STATE} can be any of {@link #TETHERING_STATE_OFF} or
+ * {@link #TETHERING_STATE_ON}
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+ * receive.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_TETHERING_STATE_CHANGED =
+ "android.bluetooth.action.TETHERING_STATE_CHANGED";
+
+ /**
+ * Extra for {@link #ACTION_TETHERING_STATE_CHANGED} intent
+ * The tethering state of the PAN profile.
+ * It can be one of {@link #TETHERING_STATE_OFF} or {@link #TETHERING_STATE_ON}.
+ */
+ public static final String EXTRA_TETHERING_STATE =
+ "android.bluetooth.extra.TETHERING_STATE";
+
/** @hide */
@IntDef({PAN_ROLE_NONE, LOCAL_NAP_ROLE, LOCAL_PANU_ROLE})
@Retention(RetentionPolicy.SOURCE)
@@ -114,6 +141,14 @@
public static final int REMOTE_PANU_ROLE = 2;
+ /** @hide **/
+ @IntDef({TETHERING_STATE_OFF, TETHERING_STATE_ON})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TetheringState{}
+
+ public static final int TETHERING_STATE_OFF = 1;
+
+ public static final int TETHERING_STATE_ON = 2;
/**
* Return codes for the connect and disconnect Bluez / Dbus calls.
*
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index 88c186c..5c1bcaf 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -110,7 +110,7 @@
* @param auth require the remote device to be authenticated
* @param encrypt require the connection to be encrypted
* @param port remote port
- * @param mitm enforce man-in-the-middle protection for authentication.
+ * @param mitm enforce person-in-the-middle protection for authentication.
* @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection
* @throws IOException On error, for example Bluetooth not available, or insufficient
* privileges
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index f774369..d41a6d0 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -128,9 +128,12 @@
private final BluetoothInputStream mInputStream;
private final BluetoothOutputStream mOutputStream;
private final ParcelUuid mUuid;
- private boolean mExcludeSdp = false; /* when true no SPP SDP record will be created */
- private boolean mAuthMitm = false; /* when true Man-in-the-middle protection will be enabled*/
- private boolean mMin16DigitPin = false; /* Minimum 16 digit pin for sec mode 2 connections */
+ /** when true no SPP SDP record will be created */
+ private boolean mExcludeSdp = false;
+ /** when true Person-in-the-middle protection will be enabled */
+ private boolean mAuthMitm = false;
+ /** Minimum 16 digit pin for sec mode 2 connections */
+ private boolean mMin16DigitPin = false;
@UnsupportedAppUsage(publicAlternatives = "Use {@link BluetoothSocket} public API instead.")
private ParcelFileDescriptor mPfd;
@UnsupportedAppUsage
@@ -190,7 +193,7 @@
* @param device remote device that this socket can connect to
* @param port remote port
* @param uuid SDP uuid
- * @param mitm enforce man-in-the-middle protection.
+ * @param mitm enforce person-in-the-middle protection.
* @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection
* @throws IOException On error, for example Bluetooth not available, or insufficient
* privileges
diff --git a/core/java/android/companion/Association.java b/core/java/android/companion/Association.java
index 3fa6a3e..06a3f2f 100644
--- a/core/java/android/companion/Association.java
+++ b/core/java/android/companion/Association.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,17 +31,21 @@
*
* @hide
*/
-@DataClass(genEqualsHashCode = true, genToString = true)
-public class Association implements Parcelable {
+@DataClass(genEqualsHashCode = true, genToString = true, genHiddenConstructor = true)
+public final class Association implements Parcelable {
- public final int userId;
- public final @NonNull String deviceAddress;
- public final @NonNull String companionAppPackage;
+ private final @UserIdInt int mUserId;
+ private final @NonNull String mDeviceMacAddress;
+ private final @NonNull String mPackageName;
+
+ /** @hide */
+ public int getUserId() {
+ return mUserId;
+ }
-
- // Code below generated by codegen v1.0.13.
+ // Code below generated by codegen v1.0.15.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -53,22 +58,39 @@
//@formatter:off
+ /**
+ * Creates a new Association.
+ *
+ * @hide
+ */
@DataClass.Generated.Member
public Association(
- int userId,
- @NonNull String deviceAddress,
- @NonNull String companionAppPackage) {
- this.userId = userId;
- this.deviceAddress = deviceAddress;
+ @UserIdInt int userId,
+ @NonNull String deviceMacAddress,
+ @NonNull String packageName) {
+ this.mUserId = userId;
com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, deviceAddress);
- this.companionAppPackage = companionAppPackage;
+ UserIdInt.class, null, mUserId);
+ this.mDeviceMacAddress = deviceMacAddress;
com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, companionAppPackage);
+ NonNull.class, null, mDeviceMacAddress);
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
// onConstructed(); // You can define this method to get a callback
}
+ @DataClass.Generated.Member
+ public @NonNull String getDeviceMacAddress() {
+ return mDeviceMacAddress;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull String getPackageName() {
+ return mPackageName;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -76,9 +98,9 @@
// String fieldNameToString() { ... }
return "Association { " +
- "userId = " + userId + ", " +
- "deviceAddress = " + deviceAddress + ", " +
- "companionAppPackage = " + companionAppPackage +
+ "userId = " + mUserId + ", " +
+ "deviceMacAddress = " + mDeviceMacAddress + ", " +
+ "packageName = " + mPackageName +
" }";
}
@@ -95,9 +117,9 @@
Association that = (Association) o;
//noinspection PointlessBooleanExpression
return true
- && userId == that.userId
- && Objects.equals(deviceAddress, that.deviceAddress)
- && Objects.equals(companionAppPackage, that.companionAppPackage);
+ && mUserId == that.mUserId
+ && Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
+ && Objects.equals(mPackageName, that.mPackageName);
}
@Override
@@ -107,9 +129,9 @@
// int fieldNameHashCode() { ... }
int _hash = 1;
- _hash = 31 * _hash + userId;
- _hash = 31 * _hash + Objects.hashCode(deviceAddress);
- _hash = 31 * _hash + Objects.hashCode(companionAppPackage);
+ _hash = 31 * _hash + mUserId;
+ _hash = 31 * _hash + Objects.hashCode(mDeviceMacAddress);
+ _hash = 31 * _hash + Objects.hashCode(mPackageName);
return _hash;
}
@@ -119,9 +141,9 @@
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
- dest.writeInt(userId);
- dest.writeString(deviceAddress);
- dest.writeString(companionAppPackage);
+ dest.writeInt(mUserId);
+ dest.writeString(mDeviceMacAddress);
+ dest.writeString(mPackageName);
}
@Override
@@ -131,21 +153,23 @@
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
- protected Association(@NonNull Parcel in) {
+ /* package-private */ Association(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
- int _userId = in.readInt();
- String _deviceAddress = in.readString();
- String _companionAppPackage = in.readString();
+ int userId = in.readInt();
+ String deviceMacAddress = in.readString();
+ String packageName = in.readString();
- this.userId = _userId;
- this.deviceAddress = _deviceAddress;
+ this.mUserId = userId;
com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, deviceAddress);
- this.companionAppPackage = _companionAppPackage;
+ UserIdInt.class, null, mUserId);
+ this.mDeviceMacAddress = deviceMacAddress;
com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, companionAppPackage);
+ NonNull.class, null, mDeviceMacAddress);
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
// onConstructed(); // You can define this method to get a callback
}
@@ -165,10 +189,10 @@
};
@DataClass.Generated(
- time = 1573767103332L,
- codegenVersion = "1.0.13",
+ time = 1599083149942L,
+ codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/companion/Association.java",
- inputSignatures = "public final int userId\npublic final @android.annotation.NonNull java.lang.String deviceAddress\npublic final @android.annotation.NonNull java.lang.String companionAppPackage\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true)")
+ inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\npublic int getUserId()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 591a714..da4980b 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -305,6 +305,24 @@
}
}
+ /**
+ * Gets all package-device {@link Association}s for the current user.
+ *
+ * @return the associations list
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
+ public @NonNull List<Association> getAllAssociations() {
+ if (!checkFeaturePresent()) {
+ return Collections.emptyList();
+ }
+ try {
+ return mService.getAssociationsForUser(mContext.getUser().getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private boolean checkFeaturePresent() {
boolean featurePresent = mService != null;
if (!featurePresent && DEBUG) {
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index b323f58..bcb9be8 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -18,6 +18,7 @@
import android.app.PendingIntent;
import android.companion.IFindDeviceCallback;
+import android.companion.Association;
import android.companion.AssociationRequest;
import android.content.ComponentName;
@@ -35,6 +36,8 @@
in String callingPackage);
List<String> getAssociations(String callingPackage, int userId);
+ List<Association> getAssociationsForUser(int userId);
+
void disassociate(String deviceMacAddress, String callingPackage);
boolean hasNotificationAccess(in ComponentName component);
diff --git a/core/java/android/content/AutofillOptions.java b/core/java/android/content/AutofillOptions.java
index 97b33b7..80a7b16 100644
--- a/core/java/android/content/AutofillOptions.java
+++ b/core/java/android/content/AutofillOptions.java
@@ -54,12 +54,12 @@
public final boolean compatModeEnabled;
/**
- * Whether package is whitelisted for augmented autofill.
+ * Whether package is allowlisted for augmented autofill.
*/
public boolean augmentedAutofillEnabled;
/**
- * List of whitelisted activities.
+ * List of allowlisted activities.
*/
@Nullable
public ArraySet<ComponentName> whitelistedActivitiesForAugmentedAutofill;
@@ -82,7 +82,7 @@
}
/**
- * Returns whether activity is whitelisted for augmented autofill.
+ * Returns whether activity is allowlisted for augmented autofill.
*/
public boolean isAugmentedAutofillEnabled(@NonNull Context context) {
if (!augmentedAutofillEnabled) return false;
diff --git a/core/java/android/content/ContentCaptureOptions.java b/core/java/android/content/ContentCaptureOptions.java
index cb2142c..ef49e02 100644
--- a/core/java/android/content/ContentCaptureOptions.java
+++ b/core/java/android/content/ContentCaptureOptions.java
@@ -69,7 +69,7 @@
public final int logHistorySize;
/**
- * List of activities explicitly whitelisted for content capture (or {@code null} if whitelisted
+ * List of activities explicitly allowlisted for content capture (or {@code null} if allowlisted
* for all acitivites in the package).
*/
@Nullable
@@ -147,7 +147,7 @@
/** @hide */
@VisibleForTesting
public boolean isWhitelisted(@NonNull Context context) {
- if (whitelistedComponents == null) return true; // whole package is whitelisted
+ if (whitelistedComponents == null) return true; // whole package is allowlisted
final ContentCaptureClient client = context.getContentCaptureClient();
if (client == null) {
// Shouldn't happen, but it doesn't hurt to check...
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index b5eb043..c39f176 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -776,6 +776,10 @@
private static final int REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS =
CONTENT_PROVIDER_READY_TIMEOUT_MILLIS + CONTENT_PROVIDER_TIMEOUT_MILLIS;
+ /**
+ * Note: passing a {@code null} context here could lead to unexpected behavior in certain
+ * ContentResolver APIs so it is highly recommended to pass a non-null context here.
+ */
public ContentResolver(@Nullable Context context) {
this(context, null);
}
@@ -3384,12 +3388,12 @@
}
/**
- * Gets the master auto-sync setting that applies to all the providers and accounts.
+ * Gets the global auto-sync setting that applies to all the providers and accounts.
* If this is false then the per-provider auto-sync setting is ignored.
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#READ_SYNC_SETTINGS}.
*
- * @return the master auto-sync setting that applies to all the providers and accounts
+ * @return the global auto-sync setting that applies to all the providers and accounts
*/
public static boolean getMasterSyncAutomatically() {
try {
@@ -3412,12 +3416,12 @@
}
/**
- * Sets the master auto-sync setting that applies to all the providers and accounts.
+ * Sets the global auto-sync setting that applies to all the providers and accounts.
* If this is false then the per-provider auto-sync setting is ignored.
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}.
*
- * @param sync the master auto-sync setting that applies to all the providers and accounts
+ * @param sync the global auto-sync setting that applies to all the providers and accounts
*/
public static void setMasterSyncAutomatically(boolean sync) {
setMasterSyncAutomaticallyAsUser(sync, UserHandle.myUserId());
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 98f7887..38bc797 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3384,6 +3384,7 @@
/** @hide */
@StringDef(suffix = { "_SERVICE" }, value = {
POWER_SERVICE,
+ //@hide: POWER_STATS_SERVICE,
WINDOW_SERVICE,
LAYOUT_INFLATER_SERVICE,
ACCOUNT_SERVICE,
@@ -3726,6 +3727,16 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.os.PowerStatsService} for accessing power stats
+ * service.
+ *
+ * @see #getSystemService(String)
+ * @hide
+ */
+ public static final String POWER_STATS_SERVICE = "power_stats";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.os.RecoverySystem} for accessing the recovery system
* service.
*
@@ -6128,7 +6139,7 @@
}
/**
- * Gets the Content Capture options for this context, or {@code null} if it's not whitelisted.
+ * Gets the Content Capture options for this context, or {@code null} if it's not allowlisted.
*
* @hide
*/
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7eed485..35c7b96 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2861,7 +2861,7 @@
* This should <em>only</em> be used to determine when the wallpaper
* has changed to show the new wallpaper to the user. You should certainly
* never, in response to this, change the wallpaper or other attributes of
- * it such as the suggested size. That would be crazy, right? You'd cause
+ * it such as the suggested size. That would be unexpected, right? You'd cause
* all kinds of loops, especially if other apps are doing similar things,
* right? Of course. So please don't do this.
*
@@ -3975,7 +3975,7 @@
public static final String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR";
/**
- * Broadcast intent sent by the RecoverySystem to inform listeners that a master clear (wipe)
+ * Broadcast intent sent by the RecoverySystem to inform listeners that a global clear (wipe)
* is about to be performed.
* @hide
*/
diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java
index 010992c..8f08fde 100644
--- a/core/java/android/content/RestrictionEntry.java
+++ b/core/java/android/content/RestrictionEntry.java
@@ -68,7 +68,7 @@
/**
* Restriction of type "multi-select". Use this for presenting a multi-select list where more
- * than one entry can be selected, such as for choosing specific titles to white-list.
+ * than one entry can be selected, such as for choosing specific titles to allowlist.
* Call {@link #setChoiceEntries(String[])} and
* {@link #setChoiceValues(String[])} to set the localized list entries to present to the user
* and the corresponding values, respectively.
diff --git a/core/java/android/content/integrity/AppIntegrityManager.java b/core/java/android/content/integrity/AppIntegrityManager.java
index 2869abb..4db4c73 100644
--- a/core/java/android/content/integrity/AppIntegrityManager.java
+++ b/core/java/android/content/integrity/AppIntegrityManager.java
@@ -31,7 +31,7 @@
* Class for pushing rules used to check the integrity of app installs.
*
* <p>Note: applications using methods of this class must be a system app and have their package
- * name whitelisted as an integrity rule provider. Otherwise a {@link SecurityException} will be
+ * name allowlisted as an integrity rule provider. Otherwise a {@link SecurityException} will be
* thrown.
*
* @hide
@@ -125,7 +125,7 @@
}
/**
- * Get the package names of all whitelisted rule providers.
+ * Get the package names of all allowlisted rule providers.
*
* <p>Warning: this method is only used for tests.
*
diff --git a/core/java/android/content/pm/AndroidTestBaseUpdater.java b/core/java/android/content/pm/AndroidTestBaseUpdater.java
index 1cbbdca..b6bee42 100644
--- a/core/java/android/content/pm/AndroidTestBaseUpdater.java
+++ b/core/java/android/content/pm/AndroidTestBaseUpdater.java
@@ -17,7 +17,7 @@
package android.content.pm;
/**
- * Dummy class to maintain legacy behavior of including a class in core source to toggle
+ * Placeholder class to maintain legacy behavior of including a class in core source to toggle
* whether or not a shared library is stripped at build time.
*
* @hide
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 0c6810c..d5f2c12 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -146,8 +146,15 @@
public int uiOptions = 0;
/**
- * Value for {@link #flags}: if set, this application is installed in the
- * device's system image.
+ * Value for {@link #flags}: if set, this application is installed in the device's system image.
+ * This should not be used to make security decisions. Instead, rely on
+ * {@linkplain android.content.pm.PackageManager#checkSignatures(java.lang.String,java.lang.String)
+ * signature checks} or
+ * <a href="https://developer.android.com/training/articles/security-tips#Permissions">permissions</a>.
+ *
+ * <p><b>Warning:</b> Note that does flag not behave the same as
+ * {@link android.R.attr#protectionLevel android:protectionLevel} {@code system} or
+ * {@code signatureOrSystem}.
*/
public static final int FLAG_SYSTEM = 1<<0;
@@ -2008,7 +2015,7 @@
* Updates the hidden API enforcement policy for this app from the given values, if appropriate.
*
* This will have no effect if this app is not subject to hidden API enforcement, i.e. if it
- * is on the package whitelist.
+ * is on the package allowlist.
*
* @param policy configured policy for this app, or {@link #HIDDEN_API_ENFORCEMENT_DEFAULT}
* if nothing configured.
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 3b6740e..8b411d5 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -285,7 +285,7 @@
* </ul>
*
* <p>Note that in order for the user to be able to grant the consent, the requesting package
- * must be whitelisted by the admin or the OEM and installed in the other profile. If this is
+ * must be allowlisted by the admin or the OEM and installed in the other profile. If this is
* not the case the user will be shown a message explaining why they can't grant the consent.
*
* <p>Note that user consent could already be granted if given a return value of {@code true}.
@@ -310,8 +310,8 @@
* <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li>
* <li>The user has previously consented to cross-profile communication for the calling
* package.</li>
- * <li>The calling package has either been whitelisted by default by the OEM or has been
- * explicitly whitelisted by the admin via
+ * <li>The calling package has either been allowlisted by default by the OEM or has been
+ * explicitly allowlisted by the admin via
* {@link android.app.admin.DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}.
* </li>
* </ul>
@@ -430,10 +430,10 @@
* other profile in the same profile group.
*
* <p>This differs from {@link #canConfigureInteractAcrossProfiles(String)} since it will
- * not return {@code false} if the app is not whitelisted or not installed in the other profile.
+ * not return {@code false} if the app is not allowlisted or not installed in the other profile.
*
* <p>Note that platform-signed apps that are automatically granted the permission and are not
- * whitelisted by the OEM will not be included in this list.
+ * allowlisted by the OEM will not be included in this list.
*
* @hide
*/
diff --git a/core/java/android/content/pm/IPackageLoadingProgressCallback.aidl b/core/java/android/content/pm/IPackageLoadingProgressCallback.aidl
new file mode 100644
index 0000000..8adfb7a
--- /dev/null
+++ b/core/java/android/content/pm/IPackageLoadingProgressCallback.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+/**
+ * Callbacks for Package Manager to report package loading progress to listeners.
+ * @hide
+ */
+oneway interface IPackageLoadingProgressCallback {
+ void onPackageLoadingProgressChanged(float progress);
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 1f8cee2..af2b905 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -31,6 +31,7 @@
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;
@@ -82,6 +83,11 @@
@UnsupportedAppUsage
ApplicationInfo getApplicationInfo(String packageName, int flags ,int userId);
+ /**
+ * @return the target SDK for the given package name, or -1 if it cannot be retrieved
+ */
+ int getTargetSdkVersion(String packageName);
+
@UnsupportedAppUsage
ActivityInfo getActivityInfo(in ComponentName className, int flags, int userId);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 9eb95a3..75ee9b7 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -700,7 +700,7 @@
* installer was created.
*
* <p>This will
- * {@link PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set) whitelist
+ * {@link PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set) allowlist
* all restricted permissions}.
*
* @param packageName The package to install.
@@ -1726,25 +1726,25 @@
}
/**
- * Sets which restricted permissions to be whitelisted for the app. Whitelisting
+ * Sets which restricted permissions to be allowlisted for the app. Allowlisting
* is not granting the permissions, rather it allows the app to hold permissions
- * which are otherwise restricted. Whitelisting a non restricted permission has
+ * which are otherwise restricted. Allowlisting a non restricted permission has
* no effect.
*
* <p> Permissions can be hard restricted which means that the app cannot hold
* them or soft restricted where the app can hold the permission but in a weaker
* form. Whether a permission is {@link PermissionInfo#FLAG_HARD_RESTRICTED hard
* restricted} or {@link PermissionInfo#FLAG_SOFT_RESTRICTED soft restricted}
- * depends on the permission declaration. Whitelisting a hard restricted permission
- * allows the app to hold that permission and whitelisting a soft restricted
+ * depends on the permission declaration. Allowlisting a hard restricted permission
+ * allows the app to hold that permission and allowlisting a soft restricted
* permission allows the app to hold the permission in its full, unrestricted form.
*
- * <p> Permissions can also be immutably restricted which means that the whitelist
+ * <p> Permissions can also be immutably restricted which means that the allowlist
* state of the permission can be determined only at install time and cannot be
* changed on updated or at a later point via the package manager APIs.
*
- * <p>Initially, all restricted permissions are whitelisted but you can change
- * which ones are whitelisted by calling this method or the corresponding ones
+ * <p>Initially, all restricted permissions are allowlisted but you can change
+ * which ones are allowlisted by calling this method or the corresponding ones
* on the {@link PackageManager}. Only soft or hard restricted permissions on the current
* Android version are supported and any invalid entries will be removed.
*
@@ -2469,7 +2469,7 @@
/**
* Get the value set in {@link SessionParams#setWhitelistedRestrictedPermissions(Set)}.
- * Note that if all permissions are whitelisted this method returns {@link
+ * Note that if all permissions are allowlisted this method returns {@link
* SessionParams#RESTRICTED_PERMISSIONS_ALL}.
*
* @hide
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e68df33..bbb9ab4 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4099,6 +4099,15 @@
}
/**
+ * @return The target SDK version for the given package name.
+ * @throws NameNotFoundException if a package with the given name cannot be found on the system.
+ */
+ @IntRange(from = 0)
+ public int getTargetSdkVersion(@NonNull String packageName) throws NameNotFoundException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* Retrieve all of the information we know about a particular activity
* class.
*
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index e990fd7..04e15c2 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -371,7 +371,7 @@
*
* <p>This permission is restricted immutably which means that its
* restriction state may be specified only on the first install of
- * the app and will stay in this initial whitelist state until
+ * the app and will stay in this initial allowlist state until
* the app is uninstalled.
*/
public static final int FLAG_IMMUTABLY_RESTRICTED = 1<<4;
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index e573539..c5240c2 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -63,6 +63,9 @@
/** @hide **/
public class PackageInfoWithoutStateUtils {
+ public static final String SYSTEM_DATA_PATH =
+ Environment.getDataDirectoryPath() + File.separator + "system";
+
@Nullable
public static PackageInfo generate(ParsingPackageRead pkg, int[] gids,
@PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
@@ -168,7 +171,8 @@
info.instrumentation = new InstrumentationInfo[N];
for (int i = 0; i < N; i++) {
info.instrumentation[i] = generateInstrumentationInfo(
- pkg.getInstrumentations().get(i), pkg, flags, userId);
+ pkg.getInstrumentations().get(i), pkg, flags, userId,
+ true /* assignUserFields */);
}
}
}
@@ -332,7 +336,8 @@
return null;
}
- return generateApplicationInfoUnchecked(pkg, flags, state, userId);
+ return generateApplicationInfoUnchecked(pkg, flags, state, userId,
+ true /* assignUserFields */);
}
/**
@@ -340,15 +345,23 @@
* system server.
*
* Prefer {@link #generateApplicationInfo(ParsingPackageRead, int, PackageUserState, int)}.
+ *
+ * @param assignUserFields whether to fill the returned {@link ApplicationInfo} with user
+ * specific fields. This can be skipped when building from a system
+ * server package, as there are cached strings which can be used rather
+ * than querying and concatenating the comparatively expensive
+ * {@link Environment#getDataDirectory(String)}}.
*/
@NonNull
public static ApplicationInfo generateApplicationInfoUnchecked(@NonNull ParsingPackageRead pkg,
- @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId) {
+ @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId,
+ boolean assignUserFields) {
// Make shallow copy so we can store the metadata/libraries safely
ApplicationInfo ai = pkg.toAppInfoWithoutState();
- // Init handles data directories
- // TODO(b/135203078): Consolidate the data directory logic, remove initForUser
- ai.initForUser(userId);
+
+ if (assignUserFields) {
+ assignUserFields(pkg, ai, userId);
+ }
if ((flags & PackageManager.GET_META_DATA) == 0) {
ai.metaData = null;
@@ -567,9 +580,14 @@
return generateProviderInfo(pkg, p, flags, state, null, userId);
}
+ /**
+ * @param assignUserFields see {@link #generateApplicationInfoUnchecked(ParsingPackageRead, int,
+ * PackageUserState, int, boolean)}
+ */
@Nullable
public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i,
- ParsingPackageRead pkg, @PackageManager.ComponentInfoFlags int flags, int userId) {
+ ParsingPackageRead pkg, @PackageManager.ComponentInfoFlags int flags, int userId,
+ boolean assignUserFields) {
if (i == null) return null;
InstrumentationInfo ii = new InstrumentationInfo();
@@ -585,10 +603,10 @@
ii.splitSourceDirs = pkg.getSplitCodePaths();
ii.splitPublicSourceDirs = pkg.getSplitCodePaths();
ii.splitDependencies = pkg.getSplitDependencies();
- ii.dataDir = getDataDir(pkg, userId).getAbsolutePath();
- ii.deviceProtectedDataDir = getDeviceProtectedDataDir(pkg, userId).getAbsolutePath();
- ii.credentialProtectedDataDir = getCredentialProtectedDataDir(pkg,
- userId).getAbsolutePath();
+
+ if (assignUserFields) {
+ assignUserFields(pkg, ii, userId);
+ }
if ((flags & PackageManager.GET_META_DATA) == 0) {
return ii;
@@ -770,4 +788,55 @@
return Environment.getDataUserCePackageDirectory(pkg.getVolumeUuid(), userId,
pkg.getPackageName());
}
+
+ private static void assignUserFields(ParsingPackageRead pkg, ApplicationInfo info, int userId) {
+ // This behavior is undefined for no-state ApplicationInfos when called by a public API,
+ // since the uid is never assigned by the system. It will always effectively be appId 0.
+ info.uid = UserHandle.getUid(userId, UserHandle.getAppId(info.uid));
+
+ String pkgName = pkg.getPackageName();
+ if ("android".equals(pkgName)) {
+ info.dataDir = SYSTEM_DATA_PATH;
+ return;
+ }
+
+ // For performance reasons, all these paths are built as strings
+ String baseDataDirPrefix =
+ Environment.getDataDirectoryPath(pkg.getVolumeUuid()) + File.separator;
+ String userIdPkgSuffix = File.separator + userId + File.separator + pkgName;
+ info.credentialProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_CE
+ + userIdPkgSuffix;
+ info.deviceProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_DE + userIdPkgSuffix;
+
+ if (pkg.isDefaultToDeviceProtectedStorage()
+ && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
+ info.dataDir = info.deviceProtectedDataDir;
+ } else {
+ info.dataDir = info.credentialProtectedDataDir;
+ }
+ }
+
+ private static void assignUserFields(ParsingPackageRead pkg, InstrumentationInfo info,
+ int userId) {
+ String pkgName = pkg.getPackageName();
+ if ("android".equals(pkgName)) {
+ info.dataDir = SYSTEM_DATA_PATH;
+ return;
+ }
+
+ // For performance reasons, all these paths are built as strings
+ String baseDataDirPrefix =
+ Environment.getDataDirectoryPath(pkg.getVolumeUuid()) + File.separator;
+ String userIdPkgSuffix = File.separator + userId + File.separator + pkgName;
+ info.credentialProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_CE
+ + userIdPkgSuffix;
+ info.deviceProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_DE + userIdPkgSuffix;
+
+ if (pkg.isDefaultToDeviceProtectedStorage()
+ && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
+ info.dataDir = info.deviceProtectedDataDir;
+ } else {
+ info.dataDir = info.credentialProtectedDataDir;
+ }
+ }
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 872098c..9cda4d3 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -16,6 +16,7 @@
package android.content.pm.parsing;
+import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Intent;
@@ -343,6 +344,6 @@
// TODO(b/135203078): This class no longer has access to ParsedPackage, find a replacement
// for moving to the next step
- @Deprecated
+ @CallSuper
Object hideAsParsed();
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index ed12a17..2a15e02 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -20,6 +20,8 @@
import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
+import android.annotation.CallSuper;
+import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Intent;
@@ -73,6 +75,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.UUID;
/**
* The backing data for a package that was parsed from disk.
@@ -147,7 +150,6 @@
@NonNull
protected String mBaseApkPath;
- private boolean requiredForAllUsers;
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
private String restrictedAccountType;
@@ -165,7 +167,6 @@
@DataClass.ParcelWith(ForInternedString.class)
private String overlayCategory;
private int overlayPriority;
- private boolean overlayIsStatic;
@NonNull
@DataClass.ParcelWith(ForInternedStringValueMap.class)
private Map<String, String> overlayables = emptyMap();
@@ -282,11 +283,6 @@
@DataClass.ParcelWith(ForInternedString.class)
protected String mPath;
- private boolean use32BitAbi;
- private boolean visibleToInstantApps;
-
- private boolean forceQueryable;
-
@NonNull
@DataClass.ParcelWith(ForInternedStringList.class)
private List<Intent> queriesIntents = emptyList();
@@ -331,12 +327,6 @@
private int compatibleWidthLimitDp;
private int descriptionRes;
- // Usually there's code to set this to true during parsing, but it's possible to install an APK
- // targeting <R that doesn't contain an <application> tag. That code would be skipped and never
- // assign this, so initialize this to true for those cases.
- private boolean enabled = true;
-
- private boolean crossProfile;
private int fullBackupContent;
private int iconRes;
private int installLocation = PackageParser.PARSE_DEFAULT_INSTALL_LOCATION;
@@ -374,26 +364,6 @@
@DataClass.ParcelWith(ForInternedString.class)
private String zygotePreloadName;
- private boolean externalStorage;
- private boolean baseHardwareAccelerated;
- private boolean allowBackup;
- private boolean killAfterRestore;
- private boolean restoreAnyVersion;
- private boolean fullBackupOnly;
- private boolean persistent;
- private boolean debuggable;
- private boolean vmSafeMode;
- private boolean hasCode;
- private boolean allowTaskReparenting;
- private boolean allowClearUserData;
- private boolean largeHeap;
- private boolean usesCleartextTraffic;
- private boolean supportsRtl;
- private boolean testOnly;
- private boolean multiArch;
- private boolean extractNativeLibs;
- private boolean game;
-
/**
* @see ParsingPackageRead#getResizeableActivity()
*/
@@ -401,26 +371,7 @@
@DataClass.ParcelWith(ForBoolean.class)
private Boolean resizeableActivity;
- private boolean staticSharedLibrary;
- private boolean overlay;
- private boolean isolatedSplitLoading;
- private boolean hasDomainUrls;
- private boolean profileableByShell;
- private boolean backupInForeground;
- private boolean useEmbeddedDex;
- private boolean defaultToDeviceProtectedStorage;
- private boolean directBootAware;
- private boolean partiallyDirectBootAware;
- private boolean resizeableActivityViaSdkVersion;
- private boolean allowClearUserDataOnFailedRestore;
- private boolean allowAudioPlaybackCapture;
- private boolean requestLegacyExternalStorage;
- private boolean usesNonSdkApi;
- private boolean hasFragileUserData;
- private boolean cantSaveState;
- private boolean allowNativeHeapPointerTagging;
private int autoRevokePermissions;
- private boolean preserveLegacyExternalStorage;
protected int gwpAsanMode;
@@ -428,6 +379,130 @@
@Nullable
private ArraySet<String> mimeGroups;
+ // Usually there's code to set enabled to true during parsing, but it's possible to install
+ // an APK targeting <R that doesn't contain an <application> tag. That code would be skipped
+ // and never assign this, so initialize this to true for those cases.
+ private long mBooleans = Booleans.ENABLED;
+
+ /**
+ * Flags used for a internal bitset. These flags should never be persisted or exposed outside
+ * of this class. It is expected that PackageCacher explicitly clears itself whenever the
+ * Parcelable implementation changes such that all these flags can be re-ordered or invalidated.
+ */
+ protected static class Booleans {
+ @LongDef({
+ EXTERNAL_STORAGE,
+ BASE_HARDWARE_ACCELERATED,
+ ALLOW_BACKUP,
+ KILL_AFTER_RESTORE,
+ RESTORE_ANY_VERSION,
+ FULL_BACKUP_ONLY,
+ PERSISTENT,
+ DEBUGGABLE,
+ VM_SAFE_MODE,
+ HAS_CODE,
+ ALLOW_TASK_REPARENTING,
+ ALLOW_CLEAR_USER_DATA,
+ LARGE_HEAP,
+ USES_CLEARTEXT_TRAFFIC,
+ SUPPORTS_RTL,
+ TEST_ONLY,
+ MULTI_ARCH,
+ EXTRACT_NATIVE_LIBS,
+ GAME,
+ STATIC_SHARED_LIBRARY,
+ OVERLAY,
+ ISOLATED_SPLIT_LOADING,
+ HAS_DOMAIN_URLS,
+ PROFILEABLE_BY_SHELL,
+ BACKUP_IN_FOREGROUND,
+ USE_EMBEDDED_DEX,
+ DEFAULT_TO_DEVICE_PROTECTED_STORAGE,
+ DIRECT_BOOT_AWARE,
+ PARTIALLY_DIRECT_BOOT_AWARE,
+ RESIZEABLE_ACTIVITY_VIA_SDK_VERSION,
+ ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE,
+ ALLOW_AUDIO_PLAYBACK_CAPTURE,
+ REQUEST_LEGACY_EXTERNAL_STORAGE,
+ USES_NON_SDK_API,
+ HAS_FRAGILE_USER_DATA,
+ CANT_SAVE_STATE,
+ ALLOW_NATIVE_HEAP_POINTER_TAGGING,
+ PRESERVE_LEGACY_EXTERNAL_STORAGE,
+ REQUIRED_FOR_ALL_USERS,
+ OVERLAY_IS_STATIC,
+ USE_32_BIT_ABI,
+ VISIBLE_TO_INSTANT_APPS,
+ FORCE_QUERYABLE,
+ CROSS_PROFILE,
+ ENABLED,
+ })
+ public @interface Values {}
+ private static final long EXTERNAL_STORAGE = 1L;
+ private static final long BASE_HARDWARE_ACCELERATED = 1L << 1;
+ private static final long ALLOW_BACKUP = 1L << 2;
+ private static final long KILL_AFTER_RESTORE = 1L << 3;
+ private static final long RESTORE_ANY_VERSION = 1L << 4;
+ private static final long FULL_BACKUP_ONLY = 1L << 5;
+ private static final long PERSISTENT = 1L << 6;
+ private static final long DEBUGGABLE = 1L << 7;
+ private static final long VM_SAFE_MODE = 1L << 8;
+ private static final long HAS_CODE = 1L << 9;
+ private static final long ALLOW_TASK_REPARENTING = 1L << 10;
+ private static final long ALLOW_CLEAR_USER_DATA = 1L << 11;
+ private static final long LARGE_HEAP = 1L << 12;
+ private static final long USES_CLEARTEXT_TRAFFIC = 1L << 13;
+ private static final long SUPPORTS_RTL = 1L << 14;
+ private static final long TEST_ONLY = 1L << 15;
+ private static final long MULTI_ARCH = 1L << 16;
+ private static final long EXTRACT_NATIVE_LIBS = 1L << 17;
+ private static final long GAME = 1L << 18;
+ private static final long STATIC_SHARED_LIBRARY = 1L << 19;
+ private static final long OVERLAY = 1L << 20;
+ private static final long ISOLATED_SPLIT_LOADING = 1L << 21;
+ private static final long HAS_DOMAIN_URLS = 1L << 22;
+ private static final long PROFILEABLE_BY_SHELL = 1L << 23;
+ private static final long BACKUP_IN_FOREGROUND = 1L << 24;
+ private static final long USE_EMBEDDED_DEX = 1L << 25;
+ private static final long DEFAULT_TO_DEVICE_PROTECTED_STORAGE = 1L << 26;
+ private static final long DIRECT_BOOT_AWARE = 1L << 27;
+ private static final long PARTIALLY_DIRECT_BOOT_AWARE = 1L << 28;
+ private static final long RESIZEABLE_ACTIVITY_VIA_SDK_VERSION = 1L << 29;
+ private static final long ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE = 1L << 30;
+ private static final long ALLOW_AUDIO_PLAYBACK_CAPTURE = 1L << 31;
+ private static final long REQUEST_LEGACY_EXTERNAL_STORAGE = 1L << 32;
+ private static final long USES_NON_SDK_API = 1L << 33;
+ private static final long HAS_FRAGILE_USER_DATA = 1L << 34;
+ private static final long CANT_SAVE_STATE = 1L << 35;
+ private static final long ALLOW_NATIVE_HEAP_POINTER_TAGGING = 1L << 36;
+ private static final long PRESERVE_LEGACY_EXTERNAL_STORAGE = 1L << 37;
+ private static final long REQUIRED_FOR_ALL_USERS = 1L << 38;
+ private static final long OVERLAY_IS_STATIC = 1L << 39;
+ private static final long USE_32_BIT_ABI = 1L << 40;
+ private static final long VISIBLE_TO_INSTANT_APPS = 1L << 41;
+ private static final long FORCE_QUERYABLE = 1L << 42;
+ private static final long CROSS_PROFILE = 1L << 43;
+ private static final long ENABLED = 1L << 44;
+ }
+
+ private ParsingPackageImpl setBoolean(@Booleans.Values long flag, boolean value) {
+ if (value) {
+ mBooleans |= flag;
+ } else {
+ mBooleans &= ~flag;
+ }
+ return this;
+ }
+
+ private boolean getBoolean(@Booleans.Values long flag) {
+ return (mBooleans & flag) != 0;
+ }
+
+ // Derived fields
+ @NonNull
+ private UUID mStorageUuid;
+ private long mLongVersionCode;
+
@VisibleForTesting
public ParsingPackageImpl(@NonNull String packageName, @NonNull String baseApkPath,
@NonNull String path, @Nullable TypedArray manifestArray) {
@@ -517,10 +592,16 @@
return this;
}
+ @CallSuper
@Override
public Object hideAsParsed() {
- // There is no equivalent for core-only parsing
- throw new UnsupportedOperationException();
+ assignDerivedFields();
+ return this;
+ }
+
+ private void assignDerivedFields() {
+ mStorageUuid = StorageManager.convert(volumeUuid);
+ mLongVersionCode = PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode);
}
@Override
@@ -862,6 +943,12 @@
return this;
}
+ @Override
+ public ParsingPackageImpl setNonLocalizedLabel(@Nullable CharSequence value) {
+ nonLocalizedLabel = value == null ? null : value.toString().trim();
+ return this;
+ }
+
@NonNull
@Override
public String getProcessName() {
@@ -905,7 +992,7 @@
// appInfo.dataDir
appInfo.descriptionRes = descriptionRes;
// appInfo.deviceProtectedDataDir
- appInfo.enabled = enabled;
+ appInfo.enabled = getBoolean(Booleans.ENABLED);
// appInfo.enabledSetting
appInfo.fullBackupContent = fullBackupContent;
// TODO(b/135203078): See ParsingPackageImpl#getHiddenApiEnforcementPolicy
@@ -924,17 +1011,11 @@
appInfo.minAspectRatio = minAspectRatio;
appInfo.minSdkVersion = minSdkVersion;
appInfo.name = className;
- if (appInfo.name != null) {
- appInfo.name = appInfo.name.trim();
- }
// appInfo.nativeLibraryDir
// appInfo.nativeLibraryRootDir
// appInfo.nativeLibraryRootRequiresIsa
appInfo.networkSecurityConfigRes = networkSecurityConfigRes;
appInfo.nonLocalizedLabel = nonLocalizedLabel;
- if (appInfo.nonLocalizedLabel != null) {
- appInfo.nonLocalizedLabel = appInfo.nonLocalizedLabel.toString().trim();
- }
appInfo.packageName = packageName;
appInfo.permission = permission;
// appInfo.primaryCpuAbi
@@ -951,7 +1032,7 @@
appInfo.splitClassLoaderNames = splitClassLoaderNames;
appInfo.splitDependencies = splitDependencies;
appInfo.splitNames = splitNames;
- appInfo.storageUuid = StorageManager.convert(volumeUuid);
+ appInfo.storageUuid = mStorageUuid;
appInfo.targetSandboxVersion = targetSandboxVersion;
appInfo.targetSdkVersion = targetSdkVersion;
appInfo.taskAffinity = taskAffinity;
@@ -967,7 +1048,7 @@
appInfo.setResourcePath(mPath);
appInfo.setSplitCodePaths(splitCodePaths);
appInfo.setSplitResourcePaths(splitCodePaths);
- appInfo.setVersionCode(PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode));
+ appInfo.setVersionCode(mLongVersionCode);
return appInfo;
}
@@ -994,14 +1075,12 @@
sForInternedString.parcel(this.packageName, dest, flags);
dest.writeString(this.realPackage);
dest.writeString(this.mBaseApkPath);
- dest.writeBoolean(this.requiredForAllUsers);
dest.writeString(this.restrictedAccountType);
dest.writeString(this.requiredAccountType);
sForInternedString.parcel(this.overlayTarget, dest, flags);
dest.writeString(this.overlayTargetName);
dest.writeString(this.overlayCategory);
dest.writeInt(this.overlayPriority);
- dest.writeBoolean(this.overlayIsStatic);
sForInternedStringValueMap.parcel(this.overlayables, dest, flags);
sForInternedString.parcel(this.staticSharedLibName, dest, flags);
dest.writeLong(this.staticSharedLibVersion);
@@ -1049,9 +1128,6 @@
sForInternedString.parcel(this.volumeUuid, dest, flags);
dest.writeParcelable(this.signingDetails, flags);
dest.writeString(this.mPath);
- dest.writeBoolean(this.use32BitAbi);
- dest.writeBoolean(this.visibleToInstantApps);
- dest.writeBoolean(this.forceQueryable);
dest.writeParcelableList(this.queriesIntents, flags);
sForInternedStringList.parcel(this.queriesPackages, dest, flags);
dest.writeString(this.appComponentFactory);
@@ -1062,8 +1138,6 @@
dest.writeString(this.className);
dest.writeInt(this.compatibleWidthLimitDp);
dest.writeInt(this.descriptionRes);
- dest.writeBoolean(this.enabled);
- dest.writeBoolean(this.crossProfile);
dest.writeInt(this.fullBackupContent);
dest.writeInt(this.iconRes);
dest.writeInt(this.installLocation);
@@ -1092,52 +1166,12 @@
dest.writeIntArray(this.splitFlags);
dest.writeStringArray(this.splitNames);
dest.writeIntArray(this.splitRevisionCodes);
-
- dest.writeBoolean(this.externalStorage);
- dest.writeBoolean(this.baseHardwareAccelerated);
- dest.writeBoolean(this.allowBackup);
- dest.writeBoolean(this.killAfterRestore);
- dest.writeBoolean(this.restoreAnyVersion);
- dest.writeBoolean(this.fullBackupOnly);
- dest.writeBoolean(this.persistent);
- dest.writeBoolean(this.debuggable);
- dest.writeBoolean(this.vmSafeMode);
- dest.writeBoolean(this.hasCode);
- dest.writeBoolean(this.allowTaskReparenting);
- dest.writeBoolean(this.allowClearUserData);
- dest.writeBoolean(this.largeHeap);
- dest.writeBoolean(this.usesCleartextTraffic);
- dest.writeBoolean(this.supportsRtl);
- dest.writeBoolean(this.testOnly);
- dest.writeBoolean(this.multiArch);
- dest.writeBoolean(this.extractNativeLibs);
- dest.writeBoolean(this.game);
-
sForBoolean.parcel(this.resizeableActivity, dest, flags);
-
- dest.writeBoolean(this.staticSharedLibrary);
- dest.writeBoolean(this.overlay);
- dest.writeBoolean(this.isolatedSplitLoading);
- dest.writeBoolean(this.hasDomainUrls);
- dest.writeBoolean(this.profileableByShell);
- dest.writeBoolean(this.backupInForeground);
- dest.writeBoolean(this.useEmbeddedDex);
- dest.writeBoolean(this.defaultToDeviceProtectedStorage);
- dest.writeBoolean(this.directBootAware);
- dest.writeBoolean(this.partiallyDirectBootAware);
- dest.writeBoolean(this.resizeableActivityViaSdkVersion);
- dest.writeBoolean(this.allowClearUserDataOnFailedRestore);
- dest.writeBoolean(this.allowAudioPlaybackCapture);
- dest.writeBoolean(this.requestLegacyExternalStorage);
- dest.writeBoolean(this.usesNonSdkApi);
- dest.writeBoolean(this.hasFragileUserData);
- dest.writeBoolean(this.cantSaveState);
- dest.writeBoolean(this.allowNativeHeapPointerTagging);
dest.writeInt(this.autoRevokePermissions);
- dest.writeBoolean(this.preserveLegacyExternalStorage);
dest.writeArraySet(this.mimeGroups);
dest.writeInt(this.gwpAsanMode);
dest.writeSparseIntArray(this.minExtensionVersions);
+ dest.writeLong(this.mBooleans);
}
public ParsingPackageImpl(Parcel in) {
@@ -1158,14 +1192,12 @@
this.packageName = sForInternedString.unparcel(in);
this.realPackage = in.readString();
this.mBaseApkPath = in.readString();
- this.requiredForAllUsers = in.readBoolean();
this.restrictedAccountType = in.readString();
this.requiredAccountType = in.readString();
this.overlayTarget = sForInternedString.unparcel(in);
this.overlayTargetName = in.readString();
this.overlayCategory = in.readString();
this.overlayPriority = in.readInt();
- this.overlayIsStatic = in.readBoolean();
this.overlayables = sForInternedStringValueMap.unparcel(in);
this.staticSharedLibName = sForInternedString.unparcel(in);
this.staticSharedLibVersion = in.readLong();
@@ -1213,9 +1245,6 @@
this.volumeUuid = sForInternedString.unparcel(in);
this.signingDetails = in.readParcelable(boot);
this.mPath = in.readString();
- this.use32BitAbi = in.readBoolean();
- this.visibleToInstantApps = in.readBoolean();
- this.forceQueryable = in.readBoolean();
this.queriesIntents = in.createTypedArrayList(Intent.CREATOR);
this.queriesPackages = sForInternedStringList.unparcel(in);
this.appComponentFactory = in.readString();
@@ -1226,8 +1255,6 @@
this.className = in.readString();
this.compatibleWidthLimitDp = in.readInt();
this.descriptionRes = in.readInt();
- this.enabled = in.readBoolean();
- this.crossProfile = in.readBoolean();
this.fullBackupContent = in.readInt();
this.iconRes = in.readInt();
this.installLocation = in.readInt();
@@ -1256,50 +1283,15 @@
this.splitFlags = in.createIntArray();
this.splitNames = in.createStringArray();
this.splitRevisionCodes = in.createIntArray();
- this.externalStorage = in.readBoolean();
- this.baseHardwareAccelerated = in.readBoolean();
- this.allowBackup = in.readBoolean();
- this.killAfterRestore = in.readBoolean();
- this.restoreAnyVersion = in.readBoolean();
- this.fullBackupOnly = in.readBoolean();
- this.persistent = in.readBoolean();
- this.debuggable = in.readBoolean();
- this.vmSafeMode = in.readBoolean();
- this.hasCode = in.readBoolean();
- this.allowTaskReparenting = in.readBoolean();
- this.allowClearUserData = in.readBoolean();
- this.largeHeap = in.readBoolean();
- this.usesCleartextTraffic = in.readBoolean();
- this.supportsRtl = in.readBoolean();
- this.testOnly = in.readBoolean();
- this.multiArch = in.readBoolean();
- this.extractNativeLibs = in.readBoolean();
- this.game = in.readBoolean();
this.resizeableActivity = sForBoolean.unparcel(in);
- this.staticSharedLibrary = in.readBoolean();
- this.overlay = in.readBoolean();
- this.isolatedSplitLoading = in.readBoolean();
- this.hasDomainUrls = in.readBoolean();
- this.profileableByShell = in.readBoolean();
- this.backupInForeground = in.readBoolean();
- this.useEmbeddedDex = in.readBoolean();
- this.defaultToDeviceProtectedStorage = in.readBoolean();
- this.directBootAware = in.readBoolean();
- this.partiallyDirectBootAware = in.readBoolean();
- this.resizeableActivityViaSdkVersion = in.readBoolean();
- this.allowClearUserDataOnFailedRestore = in.readBoolean();
- this.allowAudioPlaybackCapture = in.readBoolean();
- this.requestLegacyExternalStorage = in.readBoolean();
- this.usesNonSdkApi = in.readBoolean();
- this.hasFragileUserData = in.readBoolean();
- this.cantSaveState = in.readBoolean();
- this.allowNativeHeapPointerTagging = in.readBoolean();
this.autoRevokePermissions = in.readInt();
- this.preserveLegacyExternalStorage = in.readBoolean();
this.mimeGroups = (ArraySet<String>) in.readArraySet(boot);
this.gwpAsanMode = in.readInt();
this.minExtensionVersions = in.readSparseIntArray();
+ this.mBooleans = in.readLong();
+
+ assignDerivedFields();
}
public static final Parcelable.Creator<ParsingPackageImpl> CREATOR =
@@ -1367,7 +1359,7 @@
@Override
public boolean isRequiredForAllUsers() {
- return requiredForAllUsers;
+ return getBoolean(Booleans.REQUIRED_FOR_ALL_USERS);
}
@Nullable
@@ -1407,7 +1399,7 @@
@Override
public boolean isOverlayIsStatic() {
- return overlayIsStatic;
+ return getBoolean(Booleans.OVERLAY_IS_STATIC);
}
@NonNull
@@ -1653,17 +1645,17 @@
@Override
public boolean isUse32BitAbi() {
- return use32BitAbi;
+ return getBoolean(Booleans.USE_32_BIT_ABI);
}
@Override
public boolean isVisibleToInstantApps() {
- return visibleToInstantApps;
+ return getBoolean(Booleans.VISIBLE_TO_INSTANT_APPS);
}
@Override
public boolean isForceQueryable() {
- return forceQueryable;
+ return getBoolean(Booleans.FORCE_QUERYABLE);
}
@NonNull
@@ -1766,12 +1758,12 @@
@Override
public boolean isEnabled() {
- return enabled;
+ return getBoolean(Booleans.ENABLED);
}
@Override
public boolean isCrossProfile() {
- return crossProfile;
+ return getBoolean(Booleans.CROSS_PROFILE);
}
@Override
@@ -1892,97 +1884,97 @@
@Override
public boolean isExternalStorage() {
- return externalStorage;
+ return getBoolean(Booleans.EXTERNAL_STORAGE);
}
@Override
public boolean isBaseHardwareAccelerated() {
- return baseHardwareAccelerated;
+ return getBoolean(Booleans.BASE_HARDWARE_ACCELERATED);
}
@Override
public boolean isAllowBackup() {
- return allowBackup;
+ return getBoolean(Booleans.ALLOW_BACKUP);
}
@Override
public boolean isKillAfterRestore() {
- return killAfterRestore;
+ return getBoolean(Booleans.KILL_AFTER_RESTORE);
}
@Override
public boolean isRestoreAnyVersion() {
- return restoreAnyVersion;
+ return getBoolean(Booleans.RESTORE_ANY_VERSION);
}
@Override
public boolean isFullBackupOnly() {
- return fullBackupOnly;
+ return getBoolean(Booleans.FULL_BACKUP_ONLY);
}
@Override
public boolean isPersistent() {
- return persistent;
+ return getBoolean(Booleans.PERSISTENT);
}
@Override
public boolean isDebuggable() {
- return debuggable;
+ return getBoolean(Booleans.DEBUGGABLE);
}
@Override
public boolean isVmSafeMode() {
- return vmSafeMode;
+ return getBoolean(Booleans.VM_SAFE_MODE);
}
@Override
public boolean isHasCode() {
- return hasCode;
+ return getBoolean(Booleans.HAS_CODE);
}
@Override
public boolean isAllowTaskReparenting() {
- return allowTaskReparenting;
+ return getBoolean(Booleans.ALLOW_TASK_REPARENTING);
}
@Override
public boolean isAllowClearUserData() {
- return allowClearUserData;
+ return getBoolean(Booleans.ALLOW_CLEAR_USER_DATA);
}
@Override
public boolean isLargeHeap() {
- return largeHeap;
+ return getBoolean(Booleans.LARGE_HEAP);
}
@Override
public boolean isUsesCleartextTraffic() {
- return usesCleartextTraffic;
+ return getBoolean(Booleans.USES_CLEARTEXT_TRAFFIC);
}
@Override
public boolean isSupportsRtl() {
- return supportsRtl;
+ return getBoolean(Booleans.SUPPORTS_RTL);
}
@Override
public boolean isTestOnly() {
- return testOnly;
+ return getBoolean(Booleans.TEST_ONLY);
}
@Override
public boolean isMultiArch() {
- return multiArch;
+ return getBoolean(Booleans.MULTI_ARCH);
}
@Override
public boolean isExtractNativeLibs() {
- return extractNativeLibs;
+ return getBoolean(Booleans.EXTRACT_NATIVE_LIBS);
}
@Override
public boolean isGame() {
- return game;
+ return getBoolean(Booleans.GAME);
}
/**
@@ -1996,47 +1988,47 @@
@Override
public boolean isStaticSharedLibrary() {
- return staticSharedLibrary;
+ return getBoolean(Booleans.STATIC_SHARED_LIBRARY);
}
@Override
public boolean isOverlay() {
- return overlay;
+ return getBoolean(Booleans.OVERLAY);
}
@Override
public boolean isIsolatedSplitLoading() {
- return isolatedSplitLoading;
+ return getBoolean(Booleans.ISOLATED_SPLIT_LOADING);
}
@Override
public boolean isHasDomainUrls() {
- return hasDomainUrls;
+ return getBoolean(Booleans.HAS_DOMAIN_URLS);
}
@Override
public boolean isProfileableByShell() {
- return profileableByShell;
+ return getBoolean(Booleans.PROFILEABLE_BY_SHELL);
}
@Override
public boolean isBackupInForeground() {
- return backupInForeground;
+ return getBoolean(Booleans.BACKUP_IN_FOREGROUND);
}
@Override
public boolean isUseEmbeddedDex() {
- return useEmbeddedDex;
+ return getBoolean(Booleans.USE_EMBEDDED_DEX);
}
@Override
public boolean isDefaultToDeviceProtectedStorage() {
- return defaultToDeviceProtectedStorage;
+ return getBoolean(Booleans.DEFAULT_TO_DEVICE_PROTECTED_STORAGE);
}
@Override
public boolean isDirectBootAware() {
- return directBootAware;
+ return getBoolean(Booleans.DIRECT_BOOT_AWARE);
}
@Override
@@ -2046,47 +2038,47 @@
@Override
public boolean isPartiallyDirectBootAware() {
- return partiallyDirectBootAware;
+ return getBoolean(Booleans.PARTIALLY_DIRECT_BOOT_AWARE);
}
@Override
public boolean isResizeableActivityViaSdkVersion() {
- return resizeableActivityViaSdkVersion;
+ return getBoolean(Booleans.RESIZEABLE_ACTIVITY_VIA_SDK_VERSION);
}
@Override
public boolean isAllowClearUserDataOnFailedRestore() {
- return allowClearUserDataOnFailedRestore;
+ return getBoolean(Booleans.ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE);
}
@Override
public boolean isAllowAudioPlaybackCapture() {
- return allowAudioPlaybackCapture;
+ return getBoolean(Booleans.ALLOW_AUDIO_PLAYBACK_CAPTURE);
}
@Override
public boolean isRequestLegacyExternalStorage() {
- return requestLegacyExternalStorage;
+ return getBoolean(Booleans.REQUEST_LEGACY_EXTERNAL_STORAGE);
}
@Override
public boolean isUsesNonSdkApi() {
- return usesNonSdkApi;
+ return getBoolean(Booleans.USES_NON_SDK_API);
}
@Override
public boolean isHasFragileUserData() {
- return hasFragileUserData;
+ return getBoolean(Booleans.HAS_FRAGILE_USER_DATA);
}
@Override
public boolean isCantSaveState() {
- return cantSaveState;
+ return getBoolean(Booleans.CANT_SAVE_STATE);
}
@Override
public boolean isAllowNativeHeapPointerTagging() {
- return allowNativeHeapPointerTagging;
+ return getBoolean(Booleans.ALLOW_NATIVE_HEAP_POINTER_TAGGING);
}
@Override
@@ -2096,7 +2088,7 @@
@Override
public boolean hasPreserveLegacyExternalStorage() {
- return preserveLegacyExternalStorage;
+ return getBoolean(Booleans.PRESERVE_LEGACY_EXTERNAL_STORAGE);
}
@Override
@@ -2113,8 +2105,7 @@
@Override
public ParsingPackageImpl setRequiredForAllUsers(boolean value) {
- requiredForAllUsers = value;
- return this;
+ return setBoolean(Booleans.REQUIRED_FOR_ALL_USERS, value);
}
@Override
@@ -2125,8 +2116,7 @@
@Override
public ParsingPackageImpl setOverlayIsStatic(boolean value) {
- overlayIsStatic = value;
- return this;
+ return setBoolean(Booleans.OVERLAY_IS_STATIC, value);
}
@Override
@@ -2173,20 +2163,17 @@
@Override
public ParsingPackageImpl setUse32BitAbi(boolean value) {
- use32BitAbi = value;
- return this;
+ return setBoolean(Booleans.USE_32_BIT_ABI, value);
}
@Override
public ParsingPackageImpl setVisibleToInstantApps(boolean value) {
- visibleToInstantApps = value;
- return this;
+ return setBoolean(Booleans.VISIBLE_TO_INSTANT_APPS, value);
}
@Override
public ParsingPackageImpl setForceQueryable(boolean value) {
- forceQueryable = value;
- return this;
+ return setBoolean(Booleans.FORCE_QUERYABLE, value);
}
@Override
@@ -2215,14 +2202,12 @@
@Override
public ParsingPackageImpl setEnabled(boolean value) {
- enabled = value;
- return this;
+ return setBoolean(Booleans.ENABLED, value);
}
@Override
public ParsingPackageImpl setCrossProfile(boolean value) {
- crossProfile = value;
- return this;
+ return setBoolean(Booleans.CROSS_PROFILE, value);
}
@Override
@@ -2292,12 +2277,6 @@
}
@Override
- public ParsingPackageImpl setNonLocalizedLabel(@Nullable CharSequence value) {
- nonLocalizedLabel = value;
- return this;
- }
-
- @Override
public ParsingPackageImpl setRequiresSmallestWidthDp(int value) {
requiresSmallestWidthDp = value;
return this;
@@ -2335,116 +2314,97 @@
@Override
public ParsingPackageImpl setExternalStorage(boolean value) {
- externalStorage = value;
- return this;
+ return setBoolean(Booleans.EXTERNAL_STORAGE, value);
}
@Override
public ParsingPackageImpl setBaseHardwareAccelerated(boolean value) {
- baseHardwareAccelerated = value;
- return this;
+ return setBoolean(Booleans.BASE_HARDWARE_ACCELERATED, value);
}
@Override
public ParsingPackageImpl setAllowBackup(boolean value) {
- allowBackup = value;
- return this;
+ return setBoolean(Booleans.ALLOW_BACKUP, value);
}
@Override
public ParsingPackageImpl setKillAfterRestore(boolean value) {
- killAfterRestore = value;
- return this;
+ return setBoolean(Booleans.KILL_AFTER_RESTORE, value);
}
@Override
public ParsingPackageImpl setRestoreAnyVersion(boolean value) {
- restoreAnyVersion = value;
- return this;
+ return setBoolean(Booleans.RESTORE_ANY_VERSION, value);
}
@Override
public ParsingPackageImpl setFullBackupOnly(boolean value) {
- fullBackupOnly = value;
- return this;
+ return setBoolean(Booleans.FULL_BACKUP_ONLY, value);
}
@Override
public ParsingPackageImpl setPersistent(boolean value) {
- persistent = value;
- return this;
+ return setBoolean(Booleans.PERSISTENT, value);
}
@Override
public ParsingPackageImpl setDebuggable(boolean value) {
- debuggable = value;
- return this;
+ return setBoolean(Booleans.DEBUGGABLE, value);
}
@Override
public ParsingPackageImpl setVmSafeMode(boolean value) {
- vmSafeMode = value;
- return this;
+ return setBoolean(Booleans.VM_SAFE_MODE, value);
}
@Override
public ParsingPackageImpl setHasCode(boolean value) {
- hasCode = value;
- return this;
+ return setBoolean(Booleans.HAS_CODE, value);
}
@Override
public ParsingPackageImpl setAllowTaskReparenting(boolean value) {
- allowTaskReparenting = value;
- return this;
+ return setBoolean(Booleans.ALLOW_TASK_REPARENTING, value);
}
@Override
public ParsingPackageImpl setAllowClearUserData(boolean value) {
- allowClearUserData = value;
- return this;
+ return setBoolean(Booleans.ALLOW_CLEAR_USER_DATA, value);
}
@Override
public ParsingPackageImpl setLargeHeap(boolean value) {
- largeHeap = value;
- return this;
+ return setBoolean(Booleans.LARGE_HEAP, value);
}
@Override
public ParsingPackageImpl setUsesCleartextTraffic(boolean value) {
- usesCleartextTraffic = value;
- return this;
+ return setBoolean(Booleans.USES_CLEARTEXT_TRAFFIC, value);
}
@Override
public ParsingPackageImpl setSupportsRtl(boolean value) {
- supportsRtl = value;
- return this;
+ return setBoolean(Booleans.SUPPORTS_RTL, value);
}
@Override
public ParsingPackageImpl setTestOnly(boolean value) {
- testOnly = value;
- return this;
+ return setBoolean(Booleans.TEST_ONLY, value);
}
@Override
public ParsingPackageImpl setMultiArch(boolean value) {
- multiArch = value;
- return this;
+ return setBoolean(Booleans.MULTI_ARCH, value);
}
@Override
public ParsingPackageImpl setExtractNativeLibs(boolean value) {
- extractNativeLibs = value;
- return this;
+ return setBoolean(Booleans.EXTRACT_NATIVE_LIBS, value);
}
@Override
public ParsingPackageImpl setGame(boolean value) {
- game = value;
- return this;
+ return setBoolean(Booleans.GAME, value);
}
/**
@@ -2458,56 +2418,47 @@
@Override
public ParsingPackageImpl setStaticSharedLibrary(boolean value) {
- staticSharedLibrary = value;
- return this;
+ return setBoolean(Booleans.STATIC_SHARED_LIBRARY, value);
}
@Override
public ParsingPackageImpl setOverlay(boolean value) {
- overlay = value;
- return this;
+ return setBoolean(Booleans.OVERLAY, value);
}
@Override
public ParsingPackageImpl setIsolatedSplitLoading(boolean value) {
- isolatedSplitLoading = value;
- return this;
+ return setBoolean(Booleans.ISOLATED_SPLIT_LOADING, value);
}
@Override
public ParsingPackageImpl setHasDomainUrls(boolean value) {
- hasDomainUrls = value;
- return this;
+ return setBoolean(Booleans.HAS_DOMAIN_URLS, value);
}
@Override
public ParsingPackageImpl setProfileableByShell(boolean value) {
- profileableByShell = value;
- return this;
+ return setBoolean(Booleans.PROFILEABLE_BY_SHELL, value);
}
@Override
public ParsingPackageImpl setBackupInForeground(boolean value) {
- backupInForeground = value;
- return this;
+ return setBoolean(Booleans.BACKUP_IN_FOREGROUND, value);
}
@Override
public ParsingPackageImpl setUseEmbeddedDex(boolean value) {
- useEmbeddedDex = value;
- return this;
+ return setBoolean(Booleans.USE_EMBEDDED_DEX, value);
}
@Override
public ParsingPackageImpl setDefaultToDeviceProtectedStorage(boolean value) {
- defaultToDeviceProtectedStorage = value;
- return this;
+ return setBoolean(Booleans.DEFAULT_TO_DEVICE_PROTECTED_STORAGE, value);
}
@Override
public ParsingPackageImpl setDirectBootAware(boolean value) {
- directBootAware = value;
- return this;
+ return setBoolean(Booleans.DIRECT_BOOT_AWARE, value);
}
@Override
@@ -2518,56 +2469,47 @@
@Override
public ParsingPackageImpl setPartiallyDirectBootAware(boolean value) {
- partiallyDirectBootAware = value;
- return this;
+ return setBoolean(Booleans.PARTIALLY_DIRECT_BOOT_AWARE, value);
}
@Override
public ParsingPackageImpl setResizeableActivityViaSdkVersion(boolean value) {
- resizeableActivityViaSdkVersion = value;
- return this;
+ return setBoolean(Booleans.RESIZEABLE_ACTIVITY_VIA_SDK_VERSION, value);
}
@Override
public ParsingPackageImpl setAllowClearUserDataOnFailedRestore(boolean value) {
- allowClearUserDataOnFailedRestore = value;
- return this;
+ return setBoolean(Booleans.ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE, value);
}
@Override
public ParsingPackageImpl setAllowAudioPlaybackCapture(boolean value) {
- allowAudioPlaybackCapture = value;
- return this;
+ return setBoolean(Booleans.ALLOW_AUDIO_PLAYBACK_CAPTURE, value);
}
@Override
public ParsingPackageImpl setRequestLegacyExternalStorage(boolean value) {
- requestLegacyExternalStorage = value;
- return this;
+ return setBoolean(Booleans.REQUEST_LEGACY_EXTERNAL_STORAGE, value);
}
@Override
public ParsingPackageImpl setUsesNonSdkApi(boolean value) {
- usesNonSdkApi = value;
- return this;
+ return setBoolean(Booleans.USES_NON_SDK_API, value);
}
@Override
public ParsingPackageImpl setHasFragileUserData(boolean value) {
- hasFragileUserData = value;
- return this;
+ return setBoolean(Booleans.HAS_FRAGILE_USER_DATA, value);
}
@Override
public ParsingPackageImpl setCantSaveState(boolean value) {
- cantSaveState = value;
- return this;
+ return setBoolean(Booleans.CANT_SAVE_STATE, value);
}
@Override
public ParsingPackageImpl setAllowNativeHeapPointerTagging(boolean value) {
- allowNativeHeapPointerTagging = value;
- return this;
+ return setBoolean(Booleans.ALLOW_NATIVE_HEAP_POINTER_TAGGING, value);
}
@Override
@@ -2578,8 +2520,7 @@
@Override
public ParsingPackageImpl setPreserveLegacyExternalStorage(boolean value) {
- preserveLegacyExternalStorage = value;
- return this;
+ return setBoolean(Booleans.PRESERVE_LEGACY_EXTERNAL_STORAGE, value);
}
@Override
@@ -2644,7 +2585,7 @@
@Override
public ParsingPackageImpl setClassName(@Nullable String className) {
- this.className = className;
+ this.className = className == null ? null : className.trim();
return this;
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index dbd15f5..acd6305 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -867,7 +867,7 @@
* @see ApplicationInfo#gwpAsanMode
* @see R.styleable#AndroidManifest_gwpAsanMode
*/
- public int getGwpAsanMode();
+ int getGwpAsanMode();
// TODO(b/135203078): Hide and enforce going through PackageInfoUtils
ApplicationInfo toAppInfoWithoutState();
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index bce75cd..9197020 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -185,6 +185,9 @@
ParsingPackageUtils.getSigningDetails(pkg, false /* skipVerify */));
}
+ // Need to call this to finish the parsing stage
+ pkg.hideAsParsed();
+
return input.success(pkg);
} catch (PackageParser.PackageParserException e) {
return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index b0437ac..f5e1719 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -365,7 +365,7 @@
try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) {
XmlResourceParser parser = block.newParser();
// If nativeOpenXml doesn't throw, it will always return a valid native pointer,
- // which makes newParser always return non-null. But let's be paranoid.
+ // which makes newParser always return non-null. But let's be careful.
if (parser == null) {
throw new AssertionError("block.newParser() returned a null parser");
}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 62c7b85..69dd25f 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -1058,7 +1058,7 @@
XmlResourceParser parser = block.newParser();
// If openXmlBlockAsset doesn't throw, it will always return an XmlBlock object with
// a valid native pointer, which makes newParser always return non-null. But let's
- // be paranoid.
+ // be careful.
if (parser == null) {
throw new AssertionError("block.newParser() returned a null parser");
}
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index dd34930..a9b6132 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -26,6 +26,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
import dalvik.system.CloseGuard;
@@ -141,8 +142,6 @@
/** Usage: The buffer contains a complete mipmap hierarchy */
public static final long USAGE_GPU_MIPMAP_COMPLETE = 1 << 26;
- // The approximate size of a native AHardwareBuffer object.
- private static final long NATIVE_HARDWARE_BUFFER_SIZE = 232;
/**
* Creates a new <code>HardwareBuffer</code> instance.
*
@@ -239,10 +238,10 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private HardwareBuffer(long nativeObject) {
mNativeObject = nativeObject;
-
+ long bufferSize = nEstimateSize(nativeObject);
ClassLoader loader = HardwareBuffer.class.getClassLoader();
NativeAllocationRegistry registry = new NativeAllocationRegistry(
- loader, nGetNativeFinalizer(), NATIVE_HARDWARE_BUFFER_SIZE);
+ loader, nGetNativeFinalizer(), bufferSize);
mCleaner = registry.registerNativeAllocation(this, mNativeObject);
mCloseGuard.open("close");
}
@@ -429,4 +428,6 @@
private static native long nGetUsage(long nativeObject);
private static native boolean nIsSupported(int width, int height, int format, int layers,
long usage);
+ @CriticalNative
+ private static native long nEstimateSize(long nativeObject);
}
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index b357760..fd98d37 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -33,7 +33,7 @@
/**
* No biometric methods or nothing has been enrolled.
- * Move/expose these in BiometricPrompt if we ever want to allow applications to "blacklist"
+ * Move/expose these in BiometricPrompt if we ever want to allow applications to "denylist"
* modalities when calling authenticate().
* @hide
*/
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index 52251ba..9d32a809 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -1320,6 +1320,8 @@
return ImageFormat.DEPTH16;
case HAL_PIXEL_FORMAT_RAW16:
return ImageFormat.RAW_DEPTH;
+ case HAL_PIXEL_FORMAT_RAW10:
+ return ImageFormat.RAW_DEPTH10;
case ImageFormat.JPEG:
throw new IllegalArgumentException(
"ImageFormat.JPEG is an unknown internal format");
@@ -1393,6 +1395,8 @@
return HAL_PIXEL_FORMAT_Y16;
case ImageFormat.RAW_DEPTH:
return HAL_PIXEL_FORMAT_RAW16;
+ case ImageFormat.RAW_DEPTH10:
+ return HAL_PIXEL_FORMAT_RAW10;
default:
return format;
}
@@ -1437,6 +1441,7 @@
case ImageFormat.DEPTH_POINT_CLOUD:
case ImageFormat.DEPTH16:
case ImageFormat.RAW_DEPTH:
+ case ImageFormat.RAW_DEPTH10:
return HAL_DATASPACE_DEPTH;
case ImageFormat.DEPTH_JPEG:
return HAL_DATASPACE_DYNAMIC_DEPTH;
@@ -1878,6 +1883,8 @@
return "DEPTH_JPEG";
case ImageFormat.RAW_DEPTH:
return "RAW_DEPTH";
+ case ImageFormat.RAW_DEPTH10:
+ return "RAW_DEPTH10";
case ImageFormat.PRIVATE:
return "PRIVATE";
case ImageFormat.HEIC:
diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
index ece5c28..7dc1eaa 100644
--- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java
+++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
@@ -138,6 +138,11 @@
}
/** {@hide} */
+ public String udfpsLongPressSensorType() {
+ return mContext.getResources().getString(R.string.config_dozeUdfpsLongPressSensorType);
+ }
+
+ /** {@hide} */
public boolean pulseOnLongPressEnabled(int user) {
return pulseOnLongPressAvailable() && boolSettingDefaultOff(
Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, user);
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 55afefe..27bb9e2 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -454,20 +454,20 @@
/**
* An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput}
* The original app window token is passed from client app window.
- * {@link com.android.server.inputmethod.InputMethodManagerService} creates a unique dummy
- * token to identify this window.
- * This dummy token is only valid for a single call to {@link InputMethodImpl#showSoftInput},
- * after which it is set null until next call.
+ * {@link com.android.server.inputmethod.InputMethodManagerService} creates a unique
+ * placeholder token to identify this window.
+ * This placeholder token is only valid for a single call to
+ * {@link InputMethodImpl#showSoftInput}, after which it is set null until next call.
*/
private IBinder mCurShowInputToken;
/**
* An opaque {@link Binder} token of window requesting {@link InputMethodImpl#hideSoftInput}
* The original app window token is passed from client app window.
- * {@link com.android.server.inputmethod.InputMethodManagerService} creates a unique dummy
- * token to identify this window.
- * This dummy token is only valid for a single call to {@link InputMethodImpl#hideSoftInput},
- * after which it is set {@code null} until next call.
+ * {@link com.android.server.inputmethod.InputMethodManagerService} creates a unique
+ * placeholder token to identify this window.
+ * This placeholder token is only valid for a single call to
+ * {@link InputMethodImpl#hideSoftInput}, after which it is set {@code null} until next call.
*/
private IBinder mCurHideInputToken;
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 1922b6d..e9e242e 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -84,7 +84,7 @@
* The RULE_xxx_ALL rules applies to all networks (metered or non-metered), but on
* metered networks, the RULE_xxx_METERED rules should be checked first. For example,
* if the device is on Battery Saver Mode and Data Saver Mode simulatenously, and a uid
- * is whitelisted for the former but not the latter, its status would be
+ * is allowlisted for the former but not the latter, its status would be
* RULE_REJECT_METERED | RULE_ALLOW_ALL, meaning it could have access to non-metered
* networks but not to metered networks.
*
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 8b6ac42..e511458 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -66,7 +66,7 @@
* must verify the identity of the server you are connected to.
*
* <p class="caution"><b>Most {@link SSLSocketFactory} implementations do not
- * verify the server's identity, allowing man-in-the-middle attacks.</b>
+ * verify the server's identity, allowing person-in-the-middle attacks.</b>
* This implementation does check the server's certificate hostname, but only
* for createSocket variants that specify a hostname. When using methods that
* use {@link InetAddress} or which return an unconnected socket, you MUST
@@ -176,7 +176,7 @@
* disabled, using an optional handshake timeout and SSL session cache.
*
* <p class="caution"><b>Warning:</b> Sockets created using this factory
- * are vulnerable to man-in-the-middle attacks!</p>
+ * are vulnerable to person-in-the-middle attacks!</p>
*
* @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
* for none. The socket timeout is reset to 0 after the handshake.
@@ -508,7 +508,7 @@
*
* <p class="caution"><b>Warning:</b> Hostname verification is not performed
* with this method. You MUST verify the server's identity after connecting
- * the socket to avoid man-in-the-middle attacks.</p>
+ * the socket to avoid person-in-the-middle attacks.</p>
*/
@Override
public Socket createSocket() throws IOException {
@@ -527,7 +527,7 @@
*
* <p class="caution"><b>Warning:</b> Hostname verification is not performed
* with this method. You MUST verify the server's identity after connecting
- * the socket to avoid man-in-the-middle attacks.</p>
+ * the socket to avoid person-in-the-middle attacks.</p>
*/
@Override
public Socket createSocket(InetAddress addr, int port, InetAddress localAddr, int localPort)
@@ -548,7 +548,7 @@
*
* <p class="caution"><b>Warning:</b> Hostname verification is not performed
* with this method. You MUST verify the server's identity after connecting
- * the socket to avoid man-in-the-middle attacks.</p>
+ * the socket to avoid person-in-the-middle attacks.</p>
*/
@Override
public Socket createSocket(InetAddress addr, int port) throws IOException {
diff --git a/core/java/android/net/apf/ApfCapabilities.java b/core/java/android/net/apf/ApfCapabilities.java
index b1de74e..92c5432 100644
--- a/core/java/android/net/apf/ApfCapabilities.java
+++ b/core/java/android/net/apf/ApfCapabilities.java
@@ -127,7 +127,7 @@
}
/**
- * @return An array of blacklisted EtherType, packets with EtherTypes within it will be dropped.
+ * @return An array of denylisted EtherType, packets with EtherTypes within it will be dropped.
*/
public static @NonNull int[] getApfEtherTypeBlackList() {
return Resources.getSystem().getIntArray(R.array.config_apfEthTypeBlackList);
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 7bf82e8..b138a3f 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -57,7 +57,7 @@
/**
* Activity action: ask the user to change the default
* card emulation service for a certain category. This will
- * show a dialog that asks the user whether he wants to
+ * show a dialog that asks the user whether they want to
* replace the current default service with the service
* identified with the ComponentName specified in
* {@link #EXTRA_SERVICE_COMPONENT}, for the category
@@ -113,7 +113,7 @@
*
* <p>In this mode, when using ISO-DEP card emulation with {@link HostApduService}
* or {@link OffHostApduService}, whenever an Application ID (AID) of this category
- * is selected, the user is asked which service he wants to use to handle
+ * is selected, the user is asked which service they want to use to handle
* the transaction, even if there is only one matching service.
*/
public static final int SELECTION_MODE_ALWAYS_ASK = 1;
@@ -286,7 +286,7 @@
* <p>{@link #SELECTION_MODE_PREFER_DEFAULT} the user has requested a default
* service for this category, which will be preferred.
* <p>{@link #SELECTION_MODE_ALWAYS_ASK} the user has requested to be asked
- * every time what service he would like to use in this category.
+ * every time what service they would like to use in this category.
* <p>{@link #SELECTION_MODE_ASK_IF_CONFLICT} the user will only be asked
* to pick a service if there is a conflict.
* @param category The category, for example {@link #CATEGORY_PAYMENT}
diff --git a/core/java/android/nfc/cardemulation/HostApduService.java b/core/java/android/nfc/cardemulation/HostApduService.java
index 9208d63..55d0e73 100644
--- a/core/java/android/nfc/cardemulation/HostApduService.java
+++ b/core/java/android/nfc/cardemulation/HostApduService.java
@@ -372,7 +372,7 @@
* from a remote device. A response APDU can be provided directly
* by returning a byte-array in this method. Note that in general
* response APDUs must be sent as quickly as possible, given the fact
- * that the user is likely holding his device over an NFC reader
+ * that the user is likely holding their device over an NFC reader
* when this method is called.
*
* <p class="note">If there are multiple services that have registered for the same
diff --git a/core/java/android/nfc/cardemulation/HostNfcFService.java b/core/java/android/nfc/cardemulation/HostNfcFService.java
index fd0d8ad..65b5ca7 100644
--- a/core/java/android/nfc/cardemulation/HostNfcFService.java
+++ b/core/java/android/nfc/cardemulation/HostNfcFService.java
@@ -256,7 +256,7 @@
* from a remote device. A response packet can be provided directly
* by returning a byte-array in this method. Note that in general
* response packets must be sent as quickly as possible, given the fact
- * that the user is likely holding his device over an NFC reader
+ * that the user is likely holding their device over an NFC reader
* when this method is called.
*
* <p class="note">This method is running on the main thread of your application.
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 81deba4..1d28489 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -1613,7 +1613,7 @@
return;
}
int lengthPos = parcel.dataPosition();
- parcel.writeInt(-1); // dummy, will hold length
+ parcel.writeInt(-1); // placeholder, will hold length
parcel.writeInt(BUNDLE_MAGIC);
int startPos = parcel.dataPosition();
diff --git a/core/java/android/os/BasicShellCommandHandler.java b/core/java/android/os/BasicShellCommandHandler.java
index 1ea2229..366da3d 100644
--- a/core/java/android/os/BasicShellCommandHandler.java
+++ b/core/java/android/os/BasicShellCommandHandler.java
@@ -264,6 +264,18 @@
}
/**
+ * @return all the remaining arguments in the command without moving the current position.
+ */
+ public String[] peekRemainingArgs() {
+ int remaining = getRemainingArgsCount();
+ String[] args = new String[remaining];
+ for (int pos = mArgPos; pos < mArgs.length; pos++) {
+ args[pos - mArgPos] = mArgs[pos];
+ }
+ return args;
+ }
+
+ /**
* Returns number of arguments that haven't been processed yet.
*/
public int getRemainingArgsCount() {
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index fecfd3c..fdbf79a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1782,7 +1782,7 @@
public static final int EVENT_PACKAGE_INACTIVE = 0x000f;
// Event for a package becoming active due to an interaction.
public static final int EVENT_PACKAGE_ACTIVE = 0x0010;
- // Event for a package being on the temporary whitelist.
+ // Event for a package being on the temporary allowlist.
public static final int EVENT_TEMP_WHITELIST = 0x0011;
// Event for the screen waking up.
public static final int EVENT_SCREEN_WAKE_UP = 0x0012;
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index eaf6198..085681d 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -29,6 +29,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
@@ -73,12 +74,29 @@
private static final String DIR_FILES = "files";
private static final String DIR_CACHE = "cache";
+ /**
+ * The folder name prefix for the user credential protected data directory. This is exposed for
+ * use in string path caching for {@link ApplicationInfo} objects, and should not be accessed
+ * directly otherwise. Prefer {@link #getDataUserCeDirectory(String, int)}.
+ * {@hide}
+ */
+ public static final String DIR_USER_CE = "user";
+
+ /**
+ * The folder name prefix for the user device protected data directory. This is exposed for use
+ * in string path caching for {@link ApplicationInfo} objects, and should not be accessed
+ * directly otherwise. Prefer {@link #getDataUserDeDirectory(String, int)}.
+ * {@hide}
+ */
+ public static final String DIR_USER_DE = "user_de";
+
/** {@hide} */
@Deprecated
public static final String DIRECTORY_ANDROID = DIR_ANDROID;
private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
- private static final File DIR_ANDROID_DATA = getDirectory(ENV_ANDROID_DATA, "/data");
+ private static final String DIR_ANDROID_DATA_PATH = getDirectoryPath(ENV_ANDROID_DATA, "/data");
+ private static final File DIR_ANDROID_DATA = new File(DIR_ANDROID_DATA_PATH);
private static final File DIR_ANDROID_EXPAND = getDirectory(ENV_ANDROID_EXPAND, "/mnt/expand");
private static final File DIR_ANDROID_STORAGE = getDirectory(ENV_ANDROID_STORAGE, "/storage");
private static final File DIR_DOWNLOAD_CACHE = getDirectory(ENV_DOWNLOAD_CACHE, "/cache");
@@ -357,6 +375,14 @@
return DIR_ANDROID_DATA;
}
+ /**
+ * @see #getDataDirectory()
+ * @hide
+ */
+ public static String getDataDirectoryPath() {
+ return DIR_ANDROID_DATA_PATH;
+ }
+
/** {@hide} */
public static File getDataDirectory(String volumeUuid) {
if (TextUtils.isEmpty(volumeUuid)) {
@@ -366,6 +392,15 @@
}
}
+ /** @hide */
+ public static String getDataDirectoryPath(String volumeUuid) {
+ if (TextUtils.isEmpty(volumeUuid)) {
+ return DIR_ANDROID_DATA_PATH;
+ } else {
+ return getExpandDirectory().getAbsolutePath() + File.separator + volumeUuid;
+ }
+ }
+
/** {@hide} */
public static File getExpandDirectory() {
return DIR_ANDROID_EXPAND;
@@ -489,7 +524,7 @@
/** {@hide} */
public static File getDataUserCeDirectory(String volumeUuid) {
- return new File(getDataDirectory(volumeUuid), "user");
+ return new File(getDataDirectory(volumeUuid), DIR_USER_CE);
}
/** {@hide} */
@@ -506,7 +541,7 @@
/** {@hide} */
public static File getDataUserDeDirectory(String volumeUuid) {
- return new File(getDataDirectory(volumeUuid), "user_de");
+ return new File(getDataDirectory(volumeUuid), DIR_USER_DE);
}
/** {@hide} */
@@ -1372,6 +1407,12 @@
return path == null ? new File(defaultPath) : new File(path);
}
+ @NonNull
+ static String getDirectoryPath(@NonNull String variableName, @NonNull String defaultPath) {
+ String path = System.getenv(variableName);
+ return path == null ? defaultPath : path;
+ }
+
/** {@hide} */
public static void setUserRequired(boolean userRequired) {
sUserRequired = userRequired;
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 415e5a6..1bddc49 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -33,9 +33,10 @@
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import com.android.internal.annotations.GuardedBy;
+
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
-import dalvik.system.VMRuntime;
import libcore.util.ArrayUtils;
import libcore.util.SneakyThrow;
@@ -222,9 +223,31 @@
*/
private static boolean sParcelExceptionStackTrace;
- private static final int POOL_SIZE = 6;
- private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE];
- private static final Parcel[] sHolderPool = new Parcel[POOL_SIZE];
+ private static final Object sPoolSync = new Object();
+
+ /** Next item in the linked list pool, if any */
+ @GuardedBy("sPoolSync")
+ private Parcel mPoolNext;
+
+ /** Head of a linked list pool of {@link Parcel} objects */
+ @GuardedBy("sPoolSync")
+ private static Parcel sOwnedPool;
+ /** Head of a linked list pool of {@link Parcel} objects */
+ @GuardedBy("sPoolSync")
+ private static Parcel sHolderPool;
+
+ /** Total size of pool with head at {@link #sOwnedPool} */
+ @GuardedBy("sPoolSync")
+ private static int sOwnedPoolSize = 0;
+ /** Total size of pool with head at {@link #sHolderPool} */
+ @GuardedBy("sPoolSync")
+ private static int sHolderPoolSize = 0;
+
+ /**
+ * We're willing to pool up to 32 objects, which is sized to accommodate
+ * both a data and reply Parcel for the maximum of 16 Binder threads.
+ */
+ private static final int POOL_SIZE = 32;
// Keep in sync with frameworks/native/include/private/binder/ParcelValTypes.h.
private static final int VAL_NULL = -1;
@@ -285,7 +308,7 @@
@CriticalNative
private static native int nativeDataCapacity(long nativePtr);
@FastNative
- private static native long nativeSetDataSize(long nativePtr, int size);
+ private static native void nativeSetDataSize(long nativePtr, int size);
@CriticalNative
private static native void nativeSetDataPosition(long nativePtr, int pos);
@FastNative
@@ -314,7 +337,7 @@
@FastNative
private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
@FastNative
- private static native long nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
+ private static native void nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
private static native byte[] nativeCreateByteArray(long nativePtr);
private static native boolean nativeReadByteArray(long nativePtr, byte[] dest, int destLen);
@@ -337,14 +360,14 @@
private static native FileDescriptor nativeReadFileDescriptor(long nativePtr);
private static native long nativeCreate();
- private static native long nativeFreeBuffer(long nativePtr);
+ private static native void nativeFreeBuffer(long nativePtr);
private static native void nativeDestroy(long nativePtr);
private static native byte[] nativeMarshall(long nativePtr);
- private static native long nativeUnmarshall(
+ private static native void nativeUnmarshall(
long nativePtr, byte[] data, int offset, int length);
private static native int nativeCompareData(long thisNativePtr, long otherNativePtr);
- private static native long nativeAppendFrom(
+ private static native void nativeAppendFrom(
long thisNativePtr, long otherNativePtr, int offset, int length);
@CriticalNative
private static native boolean nativeHasFileDescriptors(long nativePtr);
@@ -420,22 +443,27 @@
*/
@NonNull
public static Parcel obtain() {
- final Parcel[] pool = sOwnedPool;
- synchronized (pool) {
- Parcel p;
- for (int i=0; i<POOL_SIZE; i++) {
- p = pool[i];
- if (p != null) {
- pool[i] = null;
- if (DEBUG_RECYCLE) {
- p.mStack = new RuntimeException();
- }
- p.mReadWriteHelper = ReadWriteHelper.DEFAULT;
- return p;
- }
+ Parcel res = null;
+ synchronized (sPoolSync) {
+ if (sOwnedPool != null) {
+ res = sOwnedPool;
+ sOwnedPool = res.mPoolNext;
+ res.mPoolNext = null;
+ sOwnedPoolSize--;
}
}
- return new Parcel(0);
+
+ // When no cache found above, create from scratch; otherwise prepare the
+ // cached object to be used
+ if (res == null) {
+ res = new Parcel(0);
+ } else {
+ if (DEBUG_RECYCLE) {
+ res.mStack = new RuntimeException();
+ }
+ res.mReadWriteHelper = ReadWriteHelper.DEFAULT;
+ }
+ return res;
}
/**
@@ -446,19 +474,21 @@
if (DEBUG_RECYCLE) mStack = null;
freeBuffer();
- final Parcel[] pool;
if (mOwnsNativeParcelObject) {
- pool = sOwnedPool;
+ synchronized (sPoolSync) {
+ if (sOwnedPoolSize < POOL_SIZE) {
+ mPoolNext = sOwnedPool;
+ sOwnedPool = this;
+ sOwnedPoolSize++;
+ }
+ }
} else {
mNativePtr = 0;
- pool = sHolderPool;
- }
-
- synchronized (pool) {
- for (int i=0; i<POOL_SIZE; i++) {
- if (pool[i] == null) {
- pool[i] = this;
- return;
+ synchronized (sPoolSync) {
+ if (sHolderPoolSize < POOL_SIZE) {
+ mPoolNext = sHolderPool;
+ sHolderPool = this;
+ sHolderPoolSize++;
}
}
}
@@ -532,7 +562,7 @@
* @param size The new number of bytes in the Parcel.
*/
public final void setDataSize(int size) {
- updateNativeSize(nativeSetDataSize(mNativePtr, size));
+ nativeSetDataSize(mNativePtr, size);
}
/**
@@ -584,11 +614,11 @@
* Set the bytes in data to be the raw bytes of this Parcel.
*/
public final void unmarshall(@NonNull byte[] data, int offset, int length) {
- updateNativeSize(nativeUnmarshall(mNativePtr, data, offset, length));
+ nativeUnmarshall(mNativePtr, data, offset, length);
}
public final void appendFrom(Parcel parcel, int offset, int length) {
- updateNativeSize(nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length));
+ nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length);
}
/** @hide */
@@ -871,24 +901,7 @@
* if {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set.</p>
*/
public final void writeFileDescriptor(@NonNull FileDescriptor val) {
- updateNativeSize(nativeWriteFileDescriptor(mNativePtr, val));
- }
-
- private void updateNativeSize(long newNativeSize) {
- if (mOwnsNativeParcelObject) {
- if (newNativeSize > Integer.MAX_VALUE) {
- newNativeSize = Integer.MAX_VALUE;
- }
- if (newNativeSize != mNativeSize) {
- int delta = (int) (newNativeSize - mNativeSize);
- if (delta > 0) {
- VMRuntime.getRuntime().registerNativeAllocation(delta);
- } else {
- VMRuntime.getRuntime().registerNativeFree(-delta);
- }
- mNativeSize = newNativeSize;
- }
- }
+ nativeWriteFileDescriptor(mNativePtr, val);
}
/**
@@ -3496,22 +3509,27 @@
/** @hide */
static protected final Parcel obtain(long obj) {
- final Parcel[] pool = sHolderPool;
- synchronized (pool) {
- Parcel p;
- for (int i=0; i<POOL_SIZE; i++) {
- p = pool[i];
- if (p != null) {
- pool[i] = null;
- if (DEBUG_RECYCLE) {
- p.mStack = new RuntimeException();
- }
- p.init(obj);
- return p;
- }
+ Parcel res = null;
+ synchronized (sPoolSync) {
+ if (sHolderPool != null) {
+ res = sHolderPool;
+ sHolderPool = res.mPoolNext;
+ res.mPoolNext = null;
+ sHolderPoolSize--;
}
}
- return new Parcel(obj);
+
+ // When no cache found above, create from scratch; otherwise prepare the
+ // cached object to be used
+ if (res == null) {
+ res = new Parcel(obj);
+ } else {
+ if (DEBUG_RECYCLE) {
+ res.mStack = new RuntimeException();
+ }
+ res.init(obj);
+ }
+ return res;
}
private Parcel(long nativePtr) {
@@ -3535,7 +3553,7 @@
private void freeBuffer() {
resetSqaushingState();
if (mOwnsNativeParcelObject) {
- updateNativeSize(nativeFreeBuffer(mNativePtr));
+ nativeFreeBuffer(mNativePtr);
}
mReadWriteHelper = ReadWriteHelper.DEFAULT;
}
@@ -3545,7 +3563,6 @@
if (mNativePtr != 0) {
if (mOwnsNativeParcelObject) {
nativeDestroy(mNativePtr);
- updateNativeSize(0);
}
mNativePtr = 0;
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index ed38b3f..3265829 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -274,7 +274,6 @@
/**
* Brightness value for fully off in float.
- * TODO(brightnessfloat): rename this to BRIGHTNES_OFF and remove the integer-based constant.
* @hide
*/
public static final float BRIGHTNESS_OFF_FLOAT = -1.0f;
@@ -1860,8 +1859,8 @@
}
/**
- * Return whether the given application package name is on the device's power whitelist.
- * Apps can be placed on the whitelist through the settings UI invoked by
+ * Return whether the given application package name is on the device's power allowlist.
+ * Apps can be placed on the allowlist through the settings UI invoked by
* {@link android.provider.Settings#ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS}.
*/
public boolean isIgnoringBatteryOptimizations(String packageName) {
@@ -2296,7 +2295,7 @@
= "android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED";
/**
- * @hide Intent that is broadcast when the set of power save whitelist apps has changed.
+ * @hide Intent that is broadcast when the set of power save allowlist apps has changed.
* This broadcast is only sent to registered receivers.
*/
@SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
@@ -2304,7 +2303,7 @@
= "android.os.action.POWER_SAVE_WHITELIST_CHANGED";
/**
- * @hide Intent that is broadcast when the set of temporarily whitelisted apps has changed.
+ * @hide Intent that is broadcast when the set of temporarily allowlisted apps has changed.
* This broadcast is only sent to registered receivers.
*/
@SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index efea953..ca0981b 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -32,6 +32,7 @@
import libcore.io.IoUtils;
import java.io.FileDescriptor;
+import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeoutException;
@@ -614,7 +615,7 @@
* started.
* @param pkgDataInfoMap Map from related package names to private data directory
* volume UUID and inode number.
- * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory
+ * @param whitelistedDataInfoMap Map from allowlisted package names to private data directory
* volume UUID and inode number.
* @param bindMountAppsData whether zygote needs to mount CE and DE data.
* @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
@@ -1317,33 +1318,16 @@
*/
public static void waitForProcessDeath(int pid, int timeout)
throws InterruptedException, TimeoutException {
- FileDescriptor pidfd = null;
- if (sPidFdSupported == PIDFD_UNKNOWN) {
- int fd = -1;
+ boolean fallback = supportsPidFd();
+ if (!fallback) {
+ FileDescriptor pidfd = null;
try {
- fd = nativePidFdOpen(pid, 0);
- sPidFdSupported = PIDFD_SUPPORTED;
- } catch (ErrnoException e) {
- sPidFdSupported = e.errno != OsConstants.ENOSYS
- ? PIDFD_SUPPORTED : PIDFD_UNSUPPORTED;
- } finally {
+ final int fd = nativePidFdOpen(pid, 0);
if (fd >= 0) {
pidfd = new FileDescriptor();
pidfd.setInt$(fd);
- }
- }
- }
- boolean fallback = sPidFdSupported == PIDFD_UNSUPPORTED;
- if (!fallback) {
- try {
- if (pidfd == null) {
- int fd = nativePidFdOpen(pid, 0);
- if (fd >= 0) {
- pidfd = new FileDescriptor();
- pidfd.setInt$(fd);
- } else {
- fallback = true;
- }
+ } else {
+ fallback = true;
}
if (pidfd != null) {
StructPollfd[] fds = new StructPollfd[] {
@@ -1392,5 +1376,59 @@
throw new TimeoutException();
}
+ /**
+ * Determine whether the system supports pidfd APIs
+ *
+ * @return Returns true if the system supports pidfd APIs
+ * @hide
+ */
+ public static boolean supportsPidFd() {
+ if (sPidFdSupported == PIDFD_UNKNOWN) {
+ int fd = -1;
+ try {
+ fd = nativePidFdOpen(myPid(), 0);
+ sPidFdSupported = PIDFD_SUPPORTED;
+ } catch (ErrnoException e) {
+ sPidFdSupported = e.errno != OsConstants.ENOSYS
+ ? PIDFD_SUPPORTED : PIDFD_UNSUPPORTED;
+ } finally {
+ if (fd >= 0) {
+ final FileDescriptor f = new FileDescriptor();
+ f.setInt$(fd);
+ IoUtils.closeQuietly(f);
+ }
+ }
+ }
+ return sPidFdSupported == PIDFD_SUPPORTED;
+ }
+
+ /**
+ * Open process file descriptor for given pid.
+ *
+ * @param pid The process ID to open for
+ * @param flags Reserved, unused now, must be 0
+ * @return The process file descriptor for given pid
+ * @throws IOException if it can't be opened
+ *
+ * @hide
+ */
+ public static @Nullable FileDescriptor openPidFd(int pid, int flags) throws IOException {
+ if (!supportsPidFd()) {
+ return null;
+ }
+ if (flags != 0) {
+ throw new IllegalArgumentException();
+ }
+ try {
+ FileDescriptor pidfd = new FileDescriptor();
+ pidfd.setInt$(nativePidFdOpen(pid, flags));
+ return pidfd;
+ } catch (ErrnoException e) {
+ IOException ex = new IOException();
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+
private static native int nativePidFdOpen(int pid, int flags) throws ErrnoException;
}
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 257bc5b..6c5b04a6 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -2500,7 +2500,7 @@
* that happens while this span is active. You must call finish() on the span when done.
*
* <p>This will never return null, but on devices without debugging enabled, this may return a
- * dummy object on which the finish() method is a no-op.
+ * placeholder object on which the finish() method is a no-op.
*
* <p>TODO: add CloseGuard to this, verifying callers call finish.
*
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index 39c196d..1bdc82a 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -29,6 +29,15 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "file_patterns": ["Environment\\.java"],
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.pm.parsing.PackageInfoUserFieldsTest"
+ }
+ ]
}
],
"postsubmit": [
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index e907e22..5a48242 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -109,7 +109,7 @@
* Error code: an update failed to apply due to a mismatch in payload
* hash.
*
- * <p>Update engine does sanity checks for the given payload and its
+ * <p>Update engine does validity checks for the given payload and its
* metadata.
*/
public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 81ffefd..ddc21ab 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -830,7 +830,7 @@
public static final String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
/**
- * Specifies if a user is disallowed from adjusting the master volume. If set, the master volume
+ * Specifies if a user is disallowed from adjusting the global volume. If set, the global volume
* will be muted. This can be set by device owners from API 21 and profile owners from API 24.
* The default value is <code>false</code>.
*
@@ -1056,7 +1056,7 @@
public static final String DISALLOW_CAMERA = "no_camera";
/**
- * Specifies if a user is not allowed to unmute the device's master volume.
+ * Specifies if a user is not allowed to unmute the device's global volume.
*
* @see DevicePolicyManager#setMasterVolumeMuted(ComponentName, boolean)
* @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
index 6adba63..a1b4dc3 100644
--- a/core/java/android/os/WorkSource.java
+++ b/core/java/android/os/WorkSource.java
@@ -1133,7 +1133,7 @@
ArrayList<WorkChain> newChains = null;
ArrayList<WorkChain> goneChains = null;
- // TODO(narayan): This is a dumb O(M*N) algorithm that determines what has changed across
+ // TODO(narayan): This is a naive O(M*N) algorithm that determines what has changed across
// WorkSource objects. We can replace this with something smarter, for e.g by defining
// a Comparator between WorkChains. It's unclear whether that will be more efficient if
// the number of chains associated with a WorkSource is expected to be small
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index ffede09..9e332e9 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -333,7 +333,7 @@
* started.
* @param pkgDataInfoMap Map from related package names to private data directory
* volume UUID and inode number.
- * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory
+ * @param whitelistedDataInfoMap Map from allowlisted package names to private data directory
* volume UUID and inode number.
* @param bindMountAppsData whether zygote needs to mount CE and DE data.
* @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
@@ -615,7 +615,7 @@
* @param disabledCompatChanges a list of disabled compat changes for the process being started.
* @param pkgDataInfoMap Map from related package names to private data directory volume UUID
* and inode number.
- * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory
+ * @param whitelistedDataInfoMap Map from allowlisted package names to private data directory
* volume UUID and inode number.
* @param bindMountAppsData whether zygote needs to mount CE and DE data.
* @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index f351c7d..ad9a162 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -19,6 +19,7 @@
import android.content.pm.DataLoaderParamsParcel;
import android.content.pm.IDataLoaderStatusListener;
import android.os.incremental.IncrementalNewFileParams;
+import android.os.incremental.IStorageLoadingProgressListener;
import android.os.incremental.IStorageHealthListener;
import android.os.incremental.StorageHealthCheckParams;
@@ -133,4 +134,14 @@
* Waits until all native library extraction is done for the storage
*/
boolean waitForNativeBinariesExtraction(int storageId);
+
+ /**
+ * Register to start listening for loading progress change for a storage.
+ */
+ boolean registerLoadingProgressListener(int storageId, IStorageLoadingProgressListener listener);
+
+ /**
+ * Stop listening for the loading progress change for a storage.
+ */
+ boolean unregisterLoadingProgressListener(int storageId);
}
diff --git a/core/java/android/os/incremental/IStorageLoadingProgressListener.aidl b/core/java/android/os/incremental/IStorageLoadingProgressListener.aidl
new file mode 100644
index 0000000..efb51fd
--- /dev/null
+++ b/core/java/android/os/incremental/IStorageLoadingProgressListener.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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.incremental;
+
+/**
+ * Callbacks for Incremental Service to report storage loading progress to Package Manager Service.
+ * @hide
+ */
+oneway interface IStorageLoadingProgressListener {
+ void onStorageLoadingProgressChanged(int storageId, float progress);
+}
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 31ccf95..dcbbd71 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -69,7 +69,7 @@
@Nullable StorageHealthCheckParams healthCheckParams,
@Nullable IStorageHealthListener healthListener,
List<InstallationFileParcel> addedFiles) throws IOException {
- // TODO(b/136132412): sanity check if session should not be incremental
+ // TODO(b/136132412): validity check if session should not be incremental
IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService(
Context.INCREMENTAL_SERVICE);
if (incrementalManager == null) {
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index c7f50c9..768ef97 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -23,6 +23,8 @@
import android.content.Context;
import android.content.pm.DataLoaderParams;
import android.content.pm.IDataLoaderStatusListener;
+import android.content.pm.IPackageLoadingProgressCallback;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.SparseArray;
@@ -76,28 +78,15 @@
}
private final @Nullable IIncrementalService mService;
- @GuardedBy("mStorages")
- private final SparseArray<IncrementalStorage> mStorages = new SparseArray<>();
+
+ private final LoadingProgressCallbacks mLoadingProgressCallbacks =
+ new LoadingProgressCallbacks();
public IncrementalManager(IIncrementalService service) {
mService = service;
}
/**
- * Returns a storage object given a storage ID.
- *
- * @param storageId The storage ID to identify the storage object.
- * @return IncrementalStorage object corresponding to storage ID.
- */
- // TODO(b/136132412): remove this
- @Nullable
- public IncrementalStorage getStorage(int storageId) {
- synchronized (mStorages) {
- return mStorages.get(storageId);
- }
- }
-
- /**
* Opens or create an Incremental File System mounted directory and returns an
* IncrementalStorage object.
*
@@ -123,9 +112,6 @@
return null;
}
final IncrementalStorage storage = new IncrementalStorage(mService, id);
- synchronized (mStorages) {
- mStorages.put(id, storage);
- }
if (autoStartDataLoader) {
storage.startLoading();
}
@@ -150,9 +136,6 @@
return null;
}
final IncrementalStorage storage = new IncrementalStorage(mService, id);
- synchronized (mStorages) {
- mStorages.put(id, storage);
- }
return storage;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -173,11 +156,7 @@
if (id < 0) {
return null;
}
- final IncrementalStorage storage = new IncrementalStorage(mService, id);
- synchronized (mStorages) {
- mStorages.put(id, storage);
- }
- return storage;
+ return new IncrementalStorage(mService, id);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -263,25 +242,6 @@
}
/**
- * Closes a storage specified by the absolute path. If the path is not Incremental, do nothing.
- * Unbinds the target dir and deletes the corresponding storage instance.
- */
- public void closeStorage(@NonNull String path) {
- try {
- final int id = mService.openStorage(path);
- if (id < 0) {
- return;
- }
- mService.deleteStorage(id);
- synchronized (mStorages) {
- mStorages.remove(id);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Checks if Incremental feature is enabled on this device.
*/
public static boolean isFeatureEnabled() {
@@ -311,6 +271,149 @@
return nativeUnsafeGetFileSignature(path);
}
+ /**
+ * Closes a storage specified by the absolute path. If the path is not Incremental, do nothing.
+ * Unbinds the target dir and deletes the corresponding storage instance.
+ * Deletes the package name and associated storage id from maps.
+ */
+ public void onPackageRemoved(@NonNull String codePath) {
+ try {
+ final IncrementalStorage storage = openStorage(codePath);
+ if (storage == null) {
+ return;
+ }
+ mLoadingProgressCallbacks.cleanUpCallbacks(storage);
+ mService.deleteStorage(storage.getId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Called when a new callback wants to listen to the loading progress of an installed package.
+ * Increment the count of callbacks associated to the corresponding storage.
+ * Only register storage listener if there hasn't been any existing callback on the storage yet.
+ * @param codePath Path of the installed package. This path is on an Incremental Storage.
+ * @param callback To report loading progress to.
+ * @return True if the package name and associated storage id are valid. False otherwise.
+ */
+ public boolean registerCallback(@NonNull String codePath,
+ @NonNull IPackageLoadingProgressCallback callback) {
+ final IncrementalStorage storage = openStorage(codePath);
+ if (storage == null) {
+ // storage does not exist, package not installed
+ return false;
+ }
+ return mLoadingProgressCallbacks.registerCallback(storage, callback);
+ }
+
+ /**
+ * Called when a callback wants to stop listen to the loading progress of an installed package.
+ * Decrease the count of the callbacks on the associated to the corresponding storage.
+ * If the count becomes zero, unregister the storage listener.
+ * @param codePath Path of the installed package
+ * @return True if the package name and associated storage id are valid. False otherwise.
+ */
+ public boolean unregisterCallback(@NonNull String codePath,
+ @NonNull IPackageLoadingProgressCallback callback) {
+ final IncrementalStorage storage = openStorage(codePath);
+ if (storage == null) {
+ // storage does not exist, package not installed
+ return false;
+ }
+ return mLoadingProgressCallbacks.unregisterCallback(storage, callback);
+ }
+
+ private static class LoadingProgressCallbacks extends IStorageLoadingProgressListener.Stub {
+ @GuardedBy("mCallbacks")
+ private final SparseArray<RemoteCallbackList<IPackageLoadingProgressCallback>> mCallbacks =
+ new SparseArray<>();
+
+ // TODO(b/165841827): disable callbacks when app state changes to fully loaded
+ public void cleanUpCallbacks(@NonNull IncrementalStorage storage) {
+ final int storageId = storage.getId();
+ final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage;
+ synchronized (mCallbacks) {
+ callbacksForStorage = mCallbacks.removeReturnOld(storageId);
+ }
+ if (callbacksForStorage == null) {
+ return;
+ }
+ // Unregister all existing callbacks on this storage
+ callbacksForStorage.kill();
+ storage.unregisterLoadingProgressListener();
+ }
+
+ // TODO(b/165841827): handle reboot and app update
+ public boolean registerCallback(@NonNull IncrementalStorage storage,
+ @NonNull IPackageLoadingProgressCallback callback) {
+ final int storageId = storage.getId();
+ synchronized (mCallbacks) {
+ RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage =
+ mCallbacks.get(storageId);
+ if (callbacksForStorage == null) {
+ callbacksForStorage = new RemoteCallbackList<>();
+ mCallbacks.put(storageId, callbacksForStorage);
+ }
+ // Registration in RemoteCallbackList needs to be done first, such that when events
+ // come from Incremental Service, the callback is already registered
+ callbacksForStorage.register(callback);
+ if (callbacksForStorage.getRegisteredCallbackCount() > 1) {
+ // already listening for progress for this storage
+ return true;
+ }
+ }
+ return storage.registerLoadingProgressListener(this);
+ }
+
+ public boolean unregisterCallback(@NonNull IncrementalStorage storage,
+ @NonNull IPackageLoadingProgressCallback callback) {
+ final int storageId = storage.getId();
+ final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage;
+ synchronized (mCallbacks) {
+ callbacksForStorage = mCallbacks.get(storageId);
+ if (callbacksForStorage == null) {
+ // no callback has ever been registered on this storage
+ return false;
+ }
+ if (!callbacksForStorage.unregister(callback)) {
+ // the callback was not registered
+ return false;
+ }
+ if (callbacksForStorage.getRegisteredCallbackCount() > 0) {
+ // other callbacks are still listening on this storage
+ return true;
+ }
+ mCallbacks.delete(storageId);
+ }
+ // stop listening for this storage
+ return storage.unregisterLoadingProgressListener();
+ }
+
+ @Override
+ public void onStorageLoadingProgressChanged(int storageId, float progress) {
+ final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage;
+ synchronized (mCallbacks) {
+ callbacksForStorage = mCallbacks.get(storageId);
+ }
+ if (callbacksForStorage == null) {
+ // no callback has ever been registered on this storage
+ return;
+ }
+ final int n = callbacksForStorage.beginBroadcast();
+ // RemoteCallbackList use ArrayMap internally and it's safe to iterate this way
+ for (int i = 0; i < n; i++) {
+ final IPackageLoadingProgressCallback callback =
+ callbacksForStorage.getBroadcastItem(i);
+ try {
+ callback.onPackageLoadingProgressChanged(progress);
+ } catch (RemoteException ignored) {
+ }
+ }
+ callbacksForStorage.finishBroadcast();
+ }
+ }
+
/* Native methods */
private static native boolean nativeIsEnabled();
private static native boolean nativeIsIncrementalPath(@NonNull String path);
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index ed386f7..f835412 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -519,4 +519,29 @@
return false;
}
}
+
+ /**
+ * Register to listen to loading progress of all the files on this storage.
+ * @param listener To report progress from Incremental Service to the caller.
+ */
+ public boolean registerLoadingProgressListener(IStorageLoadingProgressListener listener) {
+ try {
+ return mService.registerLoadingProgressListener(mId, listener);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return false;
+ }
+ }
+
+ /**
+ * Unregister to stop listening to storage loading progress.
+ */
+ public boolean unregisterLoadingProgressListener() {
+ try {
+ return mService.unregisterLoadingProgressListener(mId);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return false;
+ }
+ }
}
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
index 32c80e7..77d8664 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -31,7 +31,7 @@
/**
* V4 signature fields.
- * Keep in sync with APKSig master copy.
+ * Keep in sync with APKSig authoritative copy.
* @hide
*/
public class V4Signature {
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index b9d27e9..d80a7e7 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -154,7 +154,7 @@
* Get set of permissions that have been split into more granular or dependent permissions.
*
* <p>E.g. before {@link android.os.Build.VERSION_CODES#Q} an app that was granted
- * {@link Manifest.permission#ACCESS_COARSE_LOCATION} could access he location while it was in
+ * {@link Manifest.permission#ACCESS_COARSE_LOCATION} could access the location while it was in
* foreground and background. On platforms after {@link android.os.Build.VERSION_CODES#Q}
* the location permission only grants location access while the app is in foreground. This
* would break apps that target before {@link android.os.Build.VERSION_CODES#Q}. Hence whenever
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 0cdad9f..400b312 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -46,6 +46,8 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.SomeArgs;
+import java.util.concurrent.TimeUnit;
+
/**
* Turns a {@link SeekBar} into a volume control.
* @hide
@@ -64,9 +66,15 @@
void onSampleStarting(SeekBarVolumizer sbv);
void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch);
void onMuted(boolean muted, boolean zenMuted);
+ /**
+ * Callback reporting that the seek bar is start tracking.
+ * @param sbv - The seek bar that start tracking
+ */
+ void onStartTrackingTouch(SeekBarVolumizer sbv);
}
private static final int MSG_GROUP_VOLUME_CHANGED = 1;
+ private static long sStopVolumeTime = 0L;
private final Handler mVolumeHandler = new VolumeHandler();
private AudioAttributes mAttributes;
private int mVolumeGroupId;
@@ -126,6 +134,9 @@
private static final int MSG_STOP_SAMPLE = 2;
private static final int MSG_INIT_SAMPLE = 3;
private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000;
+ private static final long SET_STREAM_VOLUME_DELAY_MS = TimeUnit.MILLISECONDS.toMillis(500);
+ private static final long START_SAMPLE_DELAY_MS = TimeUnit.MILLISECONDS.toMillis(500);
+ private static final long DURATION_TO_START_DELAYING = TimeUnit.MILLISECONDS.toMillis(2000);
private NotificationManager.Policy mNotificationPolicy;
private boolean mAllowAlarms;
@@ -314,7 +325,29 @@
if (mHandler == null) return;
mHandler.removeMessages(MSG_START_SAMPLE);
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_SAMPLE),
- isSamplePlaying() ? CHECK_RINGTONE_PLAYBACK_DELAY_MS : 0);
+ isSamplePlaying() ? CHECK_RINGTONE_PLAYBACK_DELAY_MS
+ : isDelay() ? START_SAMPLE_DELAY_MS : 0);
+ }
+
+ // After stop volume it needs to add a small delay when playing volume or set stream.
+ // It is because the call volume is from the earpiece and the alarm/ring/media
+ // is from the speaker. If play the alarm volume or set alarm stream right after stop
+ // call volume, the alarm volume on earpiece is returned then cause the volume value incorrect.
+ // It needs a small delay after stop call volume to get alarm volume on speaker.
+ // e.g. : If the ring volume has adjusted right after call volume stopped in 2 second
+ // then delay 0.5 second to set stream or play volume ringtone.
+ private boolean isDelay() {
+ final long durationTime = java.lang.System.currentTimeMillis() - sStopVolumeTime;
+ return durationTime >= 0 && durationTime < DURATION_TO_START_DELAYING;
+ }
+
+ private void setStopVolumeTime() {
+ // set the time of stop volume
+ if ((mStreamType == AudioManager.STREAM_VOICE_CALL
+ || mStreamType == AudioManager.STREAM_RING
+ || mStreamType == AudioManager.STREAM_ALARM)) {
+ sStopVolumeTime = java.lang.System.currentTimeMillis();
+ }
}
private void onStartSample() {
@@ -341,6 +374,7 @@
private void postStopSample() {
if (mHandler == null) return;
+ setStopVolumeTime();
// remove pending delayed start messages
mHandler.removeMessages(MSG_START_SAMPLE);
mHandler.removeMessages(MSG_STOP_SAMPLE);
@@ -404,10 +438,15 @@
// Do the volume changing separately to give responsive UI
mLastProgress = progress;
mHandler.removeMessages(MSG_SET_STREAM_VOLUME);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_STREAM_VOLUME));
+ mHandler.removeMessages(MSG_START_SAMPLE);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SET_STREAM_VOLUME),
+ isDelay() ? SET_STREAM_VOLUME_DELAY_MS : 0);
}
public void onStartTrackingTouch(SeekBar seekBar) {
+ if (mCallback != null) {
+ mCallback.onStartTrackingTouch(this);
+ }
}
public void onStopTrackingTouch(SeekBar seekBar) {
@@ -539,7 +578,7 @@
if (AudioManager.VOLUME_CHANGED_ACTION.equals(action)) {
int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
int streamValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
- if (hasAudioProductStrategies()) {
+ if (hasAudioProductStrategies() && !isDelay()) {
updateVolumeSlider(streamType, streamValue);
}
} else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
@@ -551,7 +590,7 @@
}
} else if (AudioManager.STREAM_DEVICES_CHANGED_ACTION.equals(action)) {
int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
- if (hasAudioProductStrategies()) {
+ if (hasAudioProductStrategies() && !isDelay()) {
int streamVolume = mAudioManager.getStreamVolume(streamType);
updateVolumeSlider(streamType, streamVolume);
} else {
@@ -559,7 +598,9 @@
if (volumeGroup != AudioVolumeGroup.DEFAULT_VOLUME_GROUP
&& volumeGroup == mVolumeGroupId) {
int streamVolume = mAudioManager.getStreamVolume(streamType);
- updateVolumeSlider(streamType, streamVolume);
+ if (!isDelay()) {
+ updateVolumeSlider(streamType, streamVolume);
+ }
}
}
} else if (NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED.equals(action)) {
diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java
index 6eb524a..563bc46 100644
--- a/core/java/android/preference/VolumePreference.java
+++ b/core/java/android/preference/VolumePreference.java
@@ -176,6 +176,11 @@
}
@Override
+ public void onStartTrackingTouch(SeekBarVolumizer sbv) {
+ // noop
+ }
+
+ @Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
if (isPersistent()) {
diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java
index e10c507..b988461 100644
--- a/core/java/android/print/PrintDocumentInfo.java
+++ b/core/java/android/print/PrintDocumentInfo.java
@@ -32,7 +32,7 @@
* purposes. This meta-data is used by the platform and print services,
* components that interact with printers. For example, this class
* contains the number of pages contained in the document it describes and
- * this number of pages is shown to the user allowing him/her to select
+ * this number of pages is shown to the user allowing them to select
* the range to print. Also a print service may optimize the printing
* process based on the content type, such as document or photo.
* <p>
diff --git a/core/java/android/printservice/recommendation/RecommendationService.java b/core/java/android/printservice/recommendation/RecommendationService.java
index 968a625..c6d4f25 100644
--- a/core/java/android/printservice/recommendation/RecommendationService.java
+++ b/core/java/android/printservice/recommendation/RecommendationService.java
@@ -74,10 +74,10 @@
@Override
public void registerCallbacks(IRecommendationServiceCallbacks callbacks) {
// The callbacks come in order of the caller on oneway calls. Hence while the caller
- // cannot know at what time the connection is made, he can know the ordering of
+ // cannot know at what time the connection is made, they can know the ordering of
// connection and disconnection.
//
- // Similar he cannot know when the disconnection is processed, hence he has to
+ // Similar they cannot know when the disconnection is processed, hence they have to
// handle callbacks after calling disconnect.
if (callbacks != null) {
mHandler.obtainMessage(MyHandler.MSG_CONNECT, callbacks).sendToTarget();
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index 17fae1c..e1aa21e 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -188,7 +188,7 @@
* notified when there is a change in the managed profile calendar provider.
*
* <p>Throw UnsupportedOperationException if another profile doesn't exist or is disabled, or
- * if the calling package is not whitelisted to access cross-profile calendar, or if the
+ * if the calling package is not allowlisted to access cross-profile calendar, or if the
* feature has been disabled by the user in Settings.
*
* @see Events#ENTERPRISE_CONTENT_URI
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 31e0a06..24cde06 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -6151,7 +6151,7 @@
* to display names as well as phone numbers. The filter argument should be passed
* as an additional path segment after this URI.
*
- * <p class="caution"><b>Caution: </b>This field deosn't sort results based on contacts
+ * <p class="caution"><b>Caution: </b>This field doesn't sort results based on contacts
* frequency. For more information, see the
* <a href="/guide/topics/providers/contacts-provider#ObsoleteData">Contacts Provider</a>
* page.
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 4e1f819..b704d66 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -165,7 +165,7 @@
public void attachInfo(Context context, ProviderInfo info) {
registerAuthority(info.authority);
- // Sanity check our setup
+ // Validity check our setup
if (!info.exported) {
throw new SecurityException("Provider must be exported");
}
diff --git a/core/java/android/provider/SearchIndexablesProvider.java b/core/java/android/provider/SearchIndexablesProvider.java
index f4d0cb4..85c0ba1 100644
--- a/core/java/android/provider/SearchIndexablesProvider.java
+++ b/core/java/android/provider/SearchIndexablesProvider.java
@@ -100,7 +100,7 @@
mMatcher.addURI(mAuthority, SearchIndexablesContract.DYNAMIC_INDEXABLES_RAW_PATH,
MATCH_DYNAMIC_RAW_CODE);
- // Sanity check our setup
+ // Validity check our setup
if (!info.exported) {
throw new SecurityException("Provider must be exported");
}
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 9f52142..79d6bb4 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -3956,9 +3956,7 @@
public static final String APN_SET_ID = "apn_set_id";
/**
- * Possible value for the {@link #APN_SET_ID} field. By default APNs will not belong to a
- * set. If the user manually selects an APN without apn set id, there is no need to
- * prioritize any specific APN set ids.
+ * Possible value for the {@link #APN_SET_ID} field. By default APNs are added to set 0.
* <p>Type: INTEGER</p>
* @hide
*/
@@ -3966,6 +3964,16 @@
public static final int NO_APN_SET_ID = 0;
/**
+ * Possible value for the {@link #APN_SET_ID} field.
+ * APNs with MATCH_ALL_APN_SET_ID will be used regardless of any set ids of
+ * the selected APN.
+ * <p>Type: INTEGER</p>
+ * @hide
+ */
+ @SystemApi
+ public static final int MATCH_ALL_APN_SET_ID = -1;
+
+ /**
* A unique carrier id associated with this APN
* {@see TelephonyManager#getSimCarrierId()}
* <p>Type: STRING</p>
diff --git a/core/java/android/se/omapi/Channel.java b/core/java/android/se/omapi/Channel.java
index 5db3c1a..90ce11a 100644
--- a/core/java/android/se/omapi/Channel.java
+++ b/core/java/android/se/omapi/Channel.java
@@ -234,7 +234,7 @@
* response of the SELECT command.
* @return <code>true</code> if new Applet was selected on this channel.
- <code>false</code> he already selected Applet stays selected on this channel.
+ <code>false</code> the already selected Applet stays selected on this channel.
*
* @throws IOException if there is a communication problem to the reader or the Secure Element.
* @throws IllegalStateException if the channel is used after being closed.
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index bc08b84..b1107a8 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -501,7 +501,7 @@
* Disables autofill for the app or activity.
*
* <p>This method is useful to optimize performance in cases where the service knows it
- * can not autofill an app—for example, when the service has a list of "blacklisted"
+ * can not autofill an app—for example, when the service has a list of "denylisted"
* apps such as office suites.
*
* <p>By default, it disables autofill for all activities in the app, unless the response is
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 95cc64a..620c457 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -204,7 +204,7 @@
* <ul>
* <li>Service does not recognize what should be autofilled.
* <li>Service does not have data to fill the request.
- * <li>Service blacklisted that app (or activity) for autofill.
+ * <li>Service denylisted that app (or activity) for autofill.
* <li>App disabled itself for autofill.
* </ul>
*
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index b1f147b..84f6028 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -231,7 +231,7 @@
/**
* Explicitly limits content capture to the given packages and activities.
*
- * <p>To reset the whitelist, call it passing {@code null} to both arguments.
+ * <p>To reset the allowlist, call it passing {@code null} to both arguments.
*
* <p>Useful when the service wants to restrict content capture to a category of apps, like
* chat apps. For example, if the service wants to support view captures on all activities of
@@ -367,7 +367,7 @@
* Notifies the service of an activity-level event that is not associated with a session.
*
* <p>This method can be used to track some high-level events for all activities, even those
- * that are not whitelisted for Content Capture.
+ * that are not allowlisted for Content Capture.
*
* @param event high-level activity event
*/
diff --git a/core/java/android/service/dreams/Sandman.java b/core/java/android/service/dreams/Sandman.java
index f2cedbc..ced2a01 100644
--- a/core/java/android/service/dreams/Sandman.java
+++ b/core/java/android/service/dreams/Sandman.java
@@ -37,7 +37,7 @@
private static final String TAG = "Sandman";
- // The sandman is eternal. No one instantiates him.
+ // The sandman is eternal. No one instantiates them.
private Sandman() {
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 0827fef..49bc65b 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -1326,7 +1326,8 @@
line2 = res.getString(R.string.zen_mode_until, formattedTime);
} else {
// display as day/time
- summary = line1 = line2 = res.getString(R.string.zen_mode_until, formattedTime);
+ summary = line1 = line2 = res.getString(R.string.zen_mode_until_next_day,
+ formattedTime);
}
final Uri id = toCountdownConditionId(time, false);
return new Condition(id, summary, line1, line2, 0, Condition.STATE_TRUE,
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 3ed7f6c..7a18538 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -834,7 +834,7 @@
// NOTE: The API currently does not allow the caller to query whether
// they are actually connected to any engine. This might fail for various
- // reasons like if the user disables all her TTS engines.
+ // reasons like if the user disables all their TTS engines.
mCurrentEngine = null;
dispatchOnInit(ERROR);
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 8f31d77..1682686 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -1001,16 +1001,43 @@
/**
* Callback invoked when an outgoing SMS is placed to an emergency number.
*
+ * This method will be called when an emergency sms is sent on any subscription.
* @param sentEmergencyNumber the emergency number {@link EmergencyNumber} the SMS is sent to.
+ *
+ * @deprecated Use {@link #onOutgoingEmergencySms(EmergencyNumber, int)}.
* @hide
*/
@SystemApi
@TestApi
+ @Deprecated
public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber) {
// default implementation empty
}
/**
+ * Smsback invoked when an outgoing sms is sent to an emergency number.
+ *
+ * This method will be called when an emergency sms is sent on any subscription,
+ * regardless of which subscription this listener was registered on.
+ *
+ * The default implementation of this method calls
+ * {@link #onOutgoingEmergencySms(EmergencyNumber)} for backwards compatibility purposes. Do
+ * not call {@code super(...)} from within your implementation unless you want
+ * {@link #onOutgoingEmergencySms(EmergencyNumber)} to be called as well.
+ *
+ * @param sentEmergencyNumber The {@link EmergencyNumber} the emergency sms was sent to.
+ * @param subscriptionId The subscription ID used to send the emergency sms.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
+ int subscriptionId) {
+ // Default implementation for backwards compatibility
+ onOutgoingEmergencySms(sentEmergencyNumber);
+ }
+
+ /**
* Callback invoked when OEM hook raw event is received on the registered subscription.
* Note, the registration subId comes from {@link TelephonyManager} object which registers
* PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
@@ -1399,13 +1426,14 @@
subscriptionId)));
}
- public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber) {
+ public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
+ int subscriptionId) {
PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
if (psl == null) return;
Binder.withCleanCallingIdentity(
() -> mExecutor.execute(
- () -> psl.onOutgoingEmergencySms(sentEmergencyNumber)));
+ () -> psl.onOutgoingEmergencySms(sentEmergencyNumber, subscriptionId)));
}
public void onPhoneCapabilityChanged(PhoneCapability capability) {
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index 5b5c854..f19e7d2 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -345,9 +345,9 @@
}
/**
- * Print the current value given the format string provided. See man
- * strftime for what means what. The final string must be less than 256
- * characters.
+ * Print the current value given the format string provided. See
+ * strftime(3) manual page for what means what. The final string must be
+ * less than 256 characters.
* @param format a string containing the desired format.
* @return a String containing the current time expressed in the current locale.
*/
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 9cc6b9f..9244647 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -44,6 +44,8 @@
/** @hide */
public static final String SETTINGS_DO_NOT_RESTORE_PRESERVED =
"settings_do_not_restore_preserved";
+ /** @hide */
+ public static final String SETTINGS_PROVIDER_MODEL = "settings_provider_model";
private static final Map<String, String> DEFAULT_FLAGS;
@@ -67,6 +69,7 @@
DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
DEFAULT_FLAGS.put("settings_silky_home", "false");
DEFAULT_FLAGS.put("settings_contextual_home", "false");
+ DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false");
}
/**
diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java
index 55542d8..eefb156 100644
--- a/core/java/android/util/LongSparseArray.java
+++ b/core/java/android/util/LongSparseArray.java
@@ -495,7 +495,7 @@
array.mKeys = source.createLongArray();
array.mValues = source.createStringArray();
- // Make sure array is sane
+ // Make sure array is valid
Preconditions.checkArgument(array.mKeys.length >= size);
Preconditions.checkArgument(array.mValues.length >= size);
diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java
index c05dd9d..f23ec91 100644
--- a/core/java/android/util/LongSparseLongArray.java
+++ b/core/java/android/util/LongSparseLongArray.java
@@ -316,7 +316,7 @@
array.mKeys = source.createLongArray();
array.mValues = source.createLongArray();
- // Make sure array is sane
+ // Make sure array is valid
Preconditions.checkArgument(array.mKeys.length >= size);
Preconditions.checkArgument(array.mValues.length >= size);
diff --git a/core/java/android/util/SparseArrayMap.java b/core/java/android/util/SparseArrayMap.java
index 3ec6b81..3287c27 100644
--- a/core/java/android/util/SparseArrayMap.java
+++ b/core/java/android/util/SparseArrayMap.java
@@ -26,16 +26,17 @@
* A sparse array of ArrayMaps, which is suitable for holding (userId, packageName)->object
* associations.
*
- * @param <T> Any class
+ * @param <K> Any class
+ * @param <V> Any class
* @hide
*/
@TestApi
-public class SparseArrayMap<T> {
- private final SparseArray<ArrayMap<String, T>> mData = new SparseArray<>();
+public class SparseArrayMap<K, V> {
+ private final SparseArray<ArrayMap<K, V>> mData = new SparseArray<>();
- /** Add an entry associating obj with the int-String pair. */
- public void add(int key, @NonNull String mapKey, @Nullable T obj) {
- ArrayMap<String, T> data = mData.get(key);
+ /** Add an entry associating obj with the int-K pair. */
+ public void add(int key, @NonNull K mapKey, @Nullable V obj) {
+ ArrayMap<K, V> data = mData.get(key);
if (data == null) {
data = new ArrayMap<>();
mData.put(key, data);
@@ -50,8 +51,8 @@
}
}
- /** Return true if the structure contains an explicit entry for the int-String pair. */
- public boolean contains(int key, @NonNull String mapKey) {
+ /** Return true if the structure contains an explicit entry for the int-K pair. */
+ public boolean contains(int key, @NonNull K mapKey) {
return mData.contains(key) && mData.get(key).containsKey(mapKey);
}
@@ -66,8 +67,8 @@
* @return Returns the value that was stored under the keys, or null if there was none.
*/
@Nullable
- public T delete(int key, @NonNull String mapKey) {
- ArrayMap<String, T> data = mData.get(key);
+ public V delete(int key, @NonNull K mapKey) {
+ ArrayMap<K, V> data = mData.get(key);
if (data != null) {
return data.remove(mapKey);
}
@@ -75,11 +76,11 @@
}
/**
- * Get the value associated with the int-String pair.
+ * Get the value associated with the int-K pair.
*/
@Nullable
- public T get(int key, @NonNull String mapKey) {
- ArrayMap<String, T> data = mData.get(key);
+ public V get(int key, @NonNull K mapKey) {
+ ArrayMap<K, V> data = mData.get(key);
if (data != null) {
return data.get(mapKey);
}
@@ -91,9 +92,9 @@
* map contains no mapping for them.
*/
@Nullable
- public T getOrDefault(int key, @NonNull String mapKey, T defaultValue) {
+ public V getOrDefault(int key, @NonNull K mapKey, V defaultValue) {
if (mData.contains(key)) {
- ArrayMap<String, T> data = mData.get(key);
+ ArrayMap<K, V> data = mData.get(key);
if (data != null && data.containsKey(mapKey)) {
return data.get(mapKey);
}
@@ -111,8 +112,8 @@
*
* @see SparseArray#indexOfKey
*/
- public int indexOfKey(int key, @NonNull String mapKey) {
- ArrayMap<String, T> data = mData.get(key);
+ public int indexOfKey(int key, @NonNull K mapKey) {
+ ArrayMap<K, V> data = mData.get(key);
if (data != null) {
return data.indexOfKey(mapKey);
}
@@ -126,7 +127,7 @@
/** Returns the map's key at the given mapIndex for the given keyIndex. */
@NonNull
- public String keyAt(int keyIndex, int mapIndex) {
+ public K keyAt(int keyIndex, int mapIndex) {
return mData.valueAt(keyIndex).keyAt(mapIndex);
}
@@ -137,20 +138,20 @@
/** Returns the number of elements in the map of the given key. */
public int numElementsForKey(int key) {
- ArrayMap<String, T> data = mData.get(key);
+ ArrayMap<K, V> data = mData.get(key);
return data == null ? 0 : data.size();
}
- /** Returns the value T at the given key and map index. */
+ /** Returns the value V at the given key and map index. */
@Nullable
- public T valueAt(int keyIndex, int mapIndex) {
+ public V valueAt(int keyIndex, int mapIndex) {
return mData.valueAt(keyIndex).valueAt(mapIndex);
}
- /** Iterate through all int-String pairs and operate on all of the values. */
- public void forEach(@NonNull Consumer<T> consumer) {
+ /** Iterate through all int-K pairs and operate on all of the values. */
+ public void forEach(@NonNull Consumer<V> consumer) {
for (int i = numMaps() - 1; i >= 0; --i) {
- ArrayMap<String, T> data = mData.valueAt(i);
+ ArrayMap<K, V> data = mData.valueAt(i);
for (int j = data.size() - 1; j >= 0; --j) {
consumer.accept(data.valueAt(j));
}
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 987edf7..042808a4 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -660,8 +660,7 @@
ThreadedRenderer.setFPSDivisor(divisor);
}
- @UnsupportedAppUsage
- void doFrame(long frameTimeNanos, int frame) {
+ void doFrame(long frameTimeNanos, int frame, long frameTimelineVsyncId) {
final long startNanos;
synchronized (mLock) {
if (!mFrameScheduled) {
@@ -711,7 +710,7 @@
}
}
- mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
+ mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, frameTimelineVsyncId);
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
}
@@ -897,7 +896,7 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME:
- doFrame(System.nanoTime(), 0);
+ doFrame(System.nanoTime(), 0, FrameInfo.INVALID_VSYNC_ID);
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
@@ -914,6 +913,7 @@
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
+ private long mFrameTimelineVsyncId;
public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
super(looper, vsyncSource, CONFIG_CHANGED_EVENT_SUPPRESS);
@@ -923,7 +923,8 @@
// the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC
// for the internal display implicitly.
@Override
- public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
+ public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
+ long frameTimelineVsyncId) {
// Post the vsync event to the Handler.
// The idea is to prevent incoming vsync events from completely starving
// the message queue. If there are no messages in the queue with timestamps
@@ -946,6 +947,7 @@
mTimestampNanos = timestampNanos;
mFrame = frame;
+ mFrameTimelineVsyncId = frameTimelineVsyncId;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
@@ -954,7 +956,7 @@
@Override
public void run() {
mHavePendingVsync = false;
- doFrame(mTimestampNanos, mFrame);
+ doFrame(mTimestampNanos, mFrame, mFrameTimelineVsyncId);
}
}
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index eaf297c..51474d3 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -154,9 +154,11 @@
* timebase.
* @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
* @param frame The frame number. Increases by one for each vertical sync interval.
+ * @param frameTimelineVsyncId The frame timeline vsync id, used to correlate a frame
+ * produced by HWUI with the timeline data stored in Surface Flinger.
*/
- @UnsupportedAppUsage
- public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
+ public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
+ long frameTimelineVsyncId) {
}
/**
@@ -198,9 +200,9 @@
// Called from native code.
@SuppressWarnings("unused")
- @UnsupportedAppUsage
- private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
- onVsync(timestampNanos, physicalDisplayId, frame);
+ private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame,
+ long frameTimelineVsyncId) {
+ onVsync(timestampNanos, physicalDisplayId, frame, frameTimelineVsyncId);
}
// Called from native code.
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index 32cc30be8..280a1c0 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -189,6 +189,7 @@
*/
@IntDef ({
Index.FLAGS,
+ Index.FRAME_TIMELINE_VSYNC_ID,
Index.INTENDED_VSYNC,
Index.VSYNC,
Index.OLDEST_INPUT_EVENT,
@@ -206,21 +207,22 @@
@Retention(RetentionPolicy.SOURCE)
private @interface Index {
int FLAGS = 0;
- int INTENDED_VSYNC = 1;
- int VSYNC = 2;
- int OLDEST_INPUT_EVENT = 3;
- int NEWEST_INPUT_EVENT = 4;
- int HANDLE_INPUT_START = 5;
- int ANIMATION_START = 6;
- int PERFORM_TRAVERSALS_START = 7;
- int DRAW_START = 8;
- int SYNC_QUEUED = 9;
- int SYNC_START = 10;
- int ISSUE_DRAW_COMMANDS_START = 11;
- int SWAP_BUFFERS = 12;
- int FRAME_COMPLETED = 13;
+ int FRAME_TIMELINE_VSYNC_ID = 1;
+ int INTENDED_VSYNC = 2;
+ int VSYNC = 3;
+ int OLDEST_INPUT_EVENT = 4;
+ int NEWEST_INPUT_EVENT = 5;
+ int HANDLE_INPUT_START = 6;
+ int ANIMATION_START = 7;
+ int PERFORM_TRAVERSALS_START = 8;
+ int DRAW_START = 9;
+ int SYNC_QUEUED = 10;
+ int SYNC_START = 11;
+ int ISSUE_DRAW_COMMANDS_START = 12;
+ int SWAP_BUFFERS = 13;
+ int FRAME_COMPLETED = 14;
- int FRAME_STATS_COUNT = 17; // must always be last
+ int FRAME_STATS_COUNT = 18; // must always be last
}
/*
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 70850d8..609f802 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -336,4 +336,17 @@
*/
void updateInputChannel(in IBinder channelToken, int displayId, in SurfaceControl surface,
int flags, int privateFlags, in Region region);
+
+ /**
+ * Transfer window focus to an embedded window if the calling window has focus.
+ *
+ * @param window - calling window owned by the caller. Window can be null if there
+ * is no host window but the caller must have permissions to create an embedded
+ * window without a host window.
+ * @param inputToken - token identifying the embedded window that should gain focus.
+ * @param grantFocus - true if focus should be granted to the embedded window, false if focus
+ * should be transferred back to the host window. If there is no host
+ * window, the system will try to find a new focus target.
+ */
+ void grantEmbeddedWindowFocus(IWindow window, in IBinder inputToken, boolean grantFocus);
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 2bd3c06..d55c25f 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -65,6 +65,8 @@
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Handle to an on-screen Surface managed by the system compositor. The SurfaceControl is
@@ -87,10 +89,10 @@
private static native void nativeWriteToParcel(long nativeObject, Parcel out);
private static native void nativeRelease(long nativeObject);
private static native void nativeDisconnect(long nativeObject);
- private static native ScreenshotHardwareBuffer nativeCaptureDisplay(
- DisplayCaptureArgs captureArgs);
- private static native ScreenshotHardwareBuffer nativeCaptureLayers(
- LayerCaptureArgs captureArgs);
+ private static native int nativeCaptureDisplay(DisplayCaptureArgs captureArgs,
+ ScreenCaptureListener captureListener);
+ private static native int nativeCaptureLayers(LayerCaptureArgs captureArgs,
+ ScreenCaptureListener captureListener);
private static native long nativeMirrorSurface(long mirrorOfObject);
private static native long nativeCreateTransaction();
private static native long nativeGetNativeTransactionFinalizer();
@@ -613,6 +615,39 @@
}
/**
+ * @hide
+ */
+ public interface ScreenCaptureListener {
+ /**
+ * The callback invoked when the screen capture is complete.
+ * @param hardwareBuffer Data containing info about the screen capture.
+ */
+ void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer);
+ }
+
+ private static class SyncScreenCaptureListener implements ScreenCaptureListener {
+ private static final int SCREENSHOT_WAIT_TIME_S = 1;
+ private ScreenshotHardwareBuffer mScreenshotHardwareBuffer;
+ private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
+
+ @Override
+ public void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer) {
+ mScreenshotHardwareBuffer = hardwareBuffer;
+ mCountDownLatch.countDown();
+ }
+
+ private ScreenshotHardwareBuffer waitForScreenshot() {
+ try {
+ mCountDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to wait for screen capture result", e);
+ }
+
+ return mScreenshotHardwareBuffer;
+ }
+ }
+
+ /**
* A common arguments class used for various screenshot requests. This contains arguments that
* are shared between {@link DisplayCaptureArgs} and {@link LayerCaptureArgs}
* @hide
@@ -687,7 +722,7 @@
/**
* The arguments class used to make display capture requests.
*
- * @see #nativeCaptureDisplay(DisplayCaptureArgs)
+ * @see #nativeCaptureDisplay(DisplayCaptureArgs, ScreenCaptureListener)
* @hide
*/
public static class DisplayCaptureArgs extends CaptureArgs {
@@ -2228,13 +2263,30 @@
}
/**
+ * @param captureArgs Arguments about how to take the screenshot
+ * @param captureListener A listener to receive the screenshot callback
+ * @hide
+ */
+ public static int captureDisplay(@NonNull DisplayCaptureArgs captureArgs,
+ @NonNull ScreenCaptureListener captureListener) {
+ return nativeCaptureDisplay(captureArgs, captureListener);
+ }
+
+ /**
* Captures all the surfaces in a display and returns a {@link ScreenshotHardwareBuffer} with
* the content.
*
* @hide
*/
public static ScreenshotHardwareBuffer captureDisplay(DisplayCaptureArgs captureArgs) {
- return nativeCaptureDisplay(captureArgs);
+ SyncScreenCaptureListener screenCaptureListener = new SyncScreenCaptureListener();
+
+ int status = captureDisplay(captureArgs, screenCaptureListener);
+ if (status != 0) {
+ return null;
+ }
+
+ return screenCaptureListener.waitForScreenshot();
}
/**
@@ -2279,14 +2331,21 @@
.setPixelFormat(format)
.build();
- return nativeCaptureLayers(captureArgs);
+ return captureLayers(captureArgs);
}
/**
* @hide
*/
public static ScreenshotHardwareBuffer captureLayers(LayerCaptureArgs captureArgs) {
- return nativeCaptureLayers(captureArgs);
+ SyncScreenCaptureListener screenCaptureListener = new SyncScreenCaptureListener();
+
+ int status = captureLayers(captureArgs, screenCaptureListener);
+ if (status != 0) {
+ return null;
+ }
+
+ return screenCaptureListener.waitForScreenshot();
}
/**
@@ -2303,7 +2362,17 @@
.setExcludeLayers(exclude)
.build();
- return nativeCaptureLayers(captureArgs);
+ return captureLayers(captureArgs);
+ }
+
+ /**
+ * @param captureArgs Arguments about how to take the screenshot
+ * @param captureListener A listener to receive the screenshot callback
+ * @hide
+ */
+ public static int captureLayers(@NonNull LayerCaptureArgs captureArgs,
+ @NonNull ScreenCaptureListener captureListener) {
+ return nativeCaptureLayers(captureArgs, captureListener);
}
/**
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 66ab3a3..4d1faf7 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -70,10 +70,13 @@
public static final class SurfacePackage implements Parcelable {
private SurfaceControl mSurfaceControl;
private final IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
+ private final IBinder mInputToken;
- SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection) {
+ SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection,
+ IBinder inputToken) {
mSurfaceControl = sc;
mAccessibilityEmbeddedConnection = connection;
+ mInputToken = inputToken;
}
private SurfacePackage(Parcel in) {
@@ -81,6 +84,7 @@
mSurfaceControl.readFromParcel(in);
mAccessibilityEmbeddedConnection = IAccessibilityEmbeddedConnection.Stub.asInterface(
in.readStrongBinder());
+ mInputToken = in.readStrongBinder();
}
/**
@@ -126,6 +130,15 @@
mSurfaceControl = null;
}
+ /**
+ * Returns an input token used which can be used to request focus on the embedded surface.
+ *
+ * @hide
+ */
+ public IBinder getInputToken() {
+ return mInputToken;
+ }
+
public static final @NonNull Creator<SurfacePackage> CREATOR
= new Creator<SurfacePackage>() {
public SurfacePackage createFromParcel(Parcel in) {
@@ -198,7 +211,8 @@
*/
public @Nullable SurfacePackage getSurfacePackage() {
if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) {
- return new SurfacePackage(mSurfaceControl, mAccessibilityEmbeddedConnection);
+ return new SurfacePackage(mSurfaceControl, mAccessibilityEmbeddedConnection,
+ mViewRoot.getInputToken());
} else {
return null;
}
@@ -210,6 +224,7 @@
@TestApi
public void setView(@NonNull View view, @NonNull WindowManager.LayoutParams attrs) {
Objects.requireNonNull(view);
+ view.setLayoutParams(attrs);
mViewRoot.setView(view, attrs, null);
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index f937bc9..b644e9e 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -506,7 +506,7 @@
// It's possible to create a SurfaceView using the default constructor and never
// attach it to a view hierarchy, this is a common use case when dealing with
// OpenGL. A developer will probably create a new GLSurfaceView, and let it manage
- // the lifecycle. Instead of attaching it to a view, he/she can just pass
+ // the lifecycle. Instead of attaching it to a view, they can just pass
// the SurfaceHolder forward, most live wallpapers do it.
if (viewRoot != null) {
viewRoot.removeSurfaceChangedCallback(this);
@@ -1846,6 +1846,23 @@
}
}
+ @Override
+ protected void onFocusChanged(boolean gainFocus, @FocusDirection int direction,
+ @Nullable Rect previouslyFocusedRect) {
+ super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+ final ViewRootImpl viewRoot = getViewRootImpl();
+ if (mSurfacePackage == null || viewRoot == null) {
+ return;
+ }
+ try {
+ viewRoot.mWindowSession.grantEmbeddedWindowFocus(viewRoot.mWindow,
+ mSurfacePackage.getInputToken(), gainFocus);
+ } catch (Exception e) {
+ Log.e(TAG, System.identityHashCode(this)
+ + "Exception requesting focus on embedded window", e);
+ }
+ }
+
/**
* Wrapper of accessibility embedded connection for embedded view hierarchy.
*/
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index f648292..fd7c2d8 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -7033,7 +7033,7 @@
// Parent asked to see how big we want to be
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
- // Child wants a specific size... let him have it
+ // Child wants a specific size... let them have it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index af839d4..e6f89a7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3092,8 +3092,7 @@
if (changedVisibility || regainedFocus) {
// Toasts are presented as notifications - don't present them as windows as well
- boolean isToast = (mWindowAttributes == null) ? false
- : (mWindowAttributes.type == TYPE_TOAST);
+ boolean isToast = mWindowAttributes.type == TYPE_TOAST;
if (!isToast) {
host.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
@@ -3258,8 +3257,7 @@
if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) {
mFullRedrawNeeded = true;
try {
- final WindowManager.LayoutParams lp = mWindowAttributes;
- final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null;
+ final Rect surfaceInsets = mWindowAttributes.surfaceInsets;
mAttachInfo.mThreadedRenderer.initializeIfNeeded(
mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
} catch (OutOfResourcesException e) {
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 368918d..b70cb01 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -452,4 +452,9 @@
return surfaceInsets != null
? attrs.height + surfaceInsets.top + surfaceInsets.bottom : attrs.height;
}
+
+ @Override
+ public void grantEmbeddedWindowFocus(IWindow callingWindow, IBinder targetInputToken,
+ boolean grantFocus) {
+ }
}
diff --git a/core/java/android/view/accessibility/IRemoteMagnificationAnimationCallback.aidl b/core/java/android/view/accessibility/IRemoteMagnificationAnimationCallback.aidl
new file mode 100644
index 0000000..946ea16
--- /dev/null
+++ b/core/java/android/view/accessibility/IRemoteMagnificationAnimationCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility;
+
+/**
+ * A callback for magnification animation result.
+ * @hide
+ */
+
+ oneway interface IRemoteMagnificationAnimationCallback {
+
+ /**
+ * Called when the animation is finished or interrupted during animating.
+ *
+ * @param success {@code true} if animating successfully with given spec or the spec did not
+ * change. Otherwise {@code false}
+ */
+ void onResult(boolean success);
+}
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
index ae853e9..ddf68fc 100644
--- a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
@@ -18,8 +18,8 @@
import android.graphics.PointF;
import android.graphics.Rect;
-import android.os.RemoteCallback;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
/**
* Interface for interaction between {@link AccessibilityManagerService}
@@ -38,10 +38,10 @@
* or {@link Float#NaN} to leave unchanged.
* @param centerY the screen-relative Y coordinate around which to center,
* or {@link Float#NaN} to leave unchanged.
- * @param endCallback The callback called when the animation is completed.
+ * @param callback The callback called when the animation is completed or interrupted.
*/
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
- in RemoteCallback endCallback);
+ in IRemoteMagnificationAnimationCallback callback);
/**
* Sets the scale of the window magnifier on specified display.
@@ -55,9 +55,10 @@
* Disables window magnification on specified display with animation.
*
* @param displayId The logical display id.
- * @param endCallback The callback called when the animation is completed.
+ * @param callback The callback called when the animation is completed or interrupted.
*/
- void disableWindowMagnification(int displayId, in RemoteCallback endCallback);
+ void disableWindowMagnification(int displayId,
+ in IRemoteMagnificationAnimationCallback callback);
/**
* Moves the window magnifier on the specified display. It has no effect while animating.
diff --git a/core/java/android/view/accessibility/MagnificationAnimationCallback.java b/core/java/android/view/accessibility/MagnificationAnimationCallback.java
index 491f7fb..bc9fb0a 100644
--- a/core/java/android/view/accessibility/MagnificationAnimationCallback.java
+++ b/core/java/android/view/accessibility/MagnificationAnimationCallback.java
@@ -28,4 +28,4 @@
* change. Otherwise {@code false}
*/
void onResult(boolean success);
-}
+}
\ No newline at end of file
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index b398cf6..44c754c 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -509,7 +509,7 @@
/**
* Views that were otherwised not important for autofill but triggered a session because the
- * context is whitelisted for augmented autofill.
+ * context is allowlisted for augmented autofill.
*/
@GuardedBy("mLock")
@Nullable private Set<AutofillId> mEnteredForAugmentedAutofillIds;
@@ -2060,7 +2060,7 @@
/**
* Explicitly limits augmented autofill to the given packages and activities.
*
- * <p>To reset the whitelist, call it passing {@code null} to both arguments.
+ * <p>To reset the allowlist, call it passing {@code null} to both arguments.
*
* <p>Useful when the service wants to restrict augmented autofill to a category of apps, like
* apps that uses addresses. For example, if the service wants to support augmented autofill on
@@ -2107,7 +2107,7 @@
}
/**
- * Notifies that a non-autofillable view was entered because the activity is whitelisted for
+ * Notifies that a non-autofillable view was entered because the activity is allowlisted for
* augmented autofill.
*
* <p>This method is necessary to set the right flag on start, so the server-side session
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 484b1c1..029552d 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -81,7 +81,7 @@
* <a href="https://source.android.com/compatibility/cdd">CDD requirements</a>.
* <li><b>Performance:</b> content capture is highly optimized to minimize its impact in the app
* jankiness and overall device system health. For example, its only enabled on apps (or even
- * specific activities from an app) that were explicitly whitelisted by the intelligence service,
+ * specific activities from an app) that were explicitly allowlisted by the intelligence service,
* and it buffers the events so they are sent in a batch to the service (see
* {@link #isContentCaptureEnabled()} for other cases when its disabled).
* </ul>
@@ -495,7 +495,7 @@
/**
* Gets the (optional) intent used to launch the service-specific settings.
*
- * <p>This method is static because it's called by Settings, which might not be whitelisted
+ * <p>This method is static because it's called by Settings, which might not be allowlisted
* for content capture (in which case the ContentCaptureManager on its context would be null).
*
* @hide
@@ -530,8 +530,8 @@
* <p>There are many reasons it could be disabled, such as:
* <ul>
* <li>App itself disabled content capture through {@link #setContentCaptureEnabled(boolean)}.
- * <li>Intelligence service did not whitelist content capture for this activity's package.
- * <li>Intelligence service did not whitelist content capture for this specific activity.
+ * <li>Intelligence service did not allowlist content capture for this activity's package.
+ * <li>Intelligence service did not allowlist content capture for this specific activity.
* <li>Intelligence service disabled content capture globally.
* <li>User disabled content capture globally through the Android Settings app.
* <li>Device manufacturer (OEM) disabled content capture globally.
@@ -566,7 +566,7 @@
public Set<ContentCaptureCondition> getContentCaptureConditions() {
// NOTE: we could cache the conditions on ContentCaptureOptions, but then it would be stick
// to the lifetime of the app. OTOH, by dynamically calling the server every time, we allow
- // the service to fine tune how long-lived apps (like browsers) are whitelisted.
+ // the service to fine tune how long-lived apps (like browsers) are allowlisted.
if (!isContentCaptureEnabled() && !mOptions.lite) return null;
final SyncResultReceiver resultReceiver = syncRun(
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 64c59e5..c633268 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -126,7 +126,7 @@
public static final int STATE_INTERNAL_ERROR = 0x100;
/**
- * Session is disabled because service didn't whitelist package or activity.
+ * Session is disabled because service didn't allowlist package or activity.
*
* @hide
*/
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index c43beea..0535365 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -397,7 +397,7 @@
+ " after " + numberEvents + " delayed events");
}
resetSession(STATE_DISABLED | STATE_NO_RESPONSE);
- // TODO(b/111276913): blacklist activity / use special flag to indicate that
+ // TODO(b/111276913): denylist activity / use special flag to indicate that
// when it's launched again
return;
}
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index c5f2299..104bc43 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -22,13 +22,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
-import android.text.Editable;
import android.text.InputType;
+import android.text.ParcelableSpan;
import android.text.TextUtils;
import android.util.Printer;
import android.view.View;
@@ -543,6 +544,9 @@
* {@code #getInitialSelectedText}, and {@code #getInitialTextBeforeCursor}. System is allowed
* to trim {@code sourceText} for various reasons while keeping the most valuable data to IMEs.
*
+ * Starting from {@link VERSION_CODES#S}, spans that do not implement {@link Parcelable} will
+ * be automatically dropped.
+ *
* <p><strong>Editor authors: </strong>Providing the initial input text helps reducing IPC calls
* for IMEs to provide many modern features right after the connection setup. We recommend
* calling this method in your implementation.
@@ -562,14 +566,16 @@
* try to include the selected text within {@code subText} to give the system best flexibility
* to choose where and how to trim {@code subText} when necessary.
*
+ * Starting from {@link VERSION_CODES#S}, spans that do not implement {@link Parcelable} will
+ * be automatically dropped.
+ *
* @param subText The input text. When it was trimmed, {@code subTextStart} must be provided
* correctly.
* @param subTextStart The position that the input text got trimmed. For example, when the
* editor wants to trim out the first 10 chars, subTextStart should be 10.
*/
public void setInitialSurroundingSubText(@NonNull CharSequence subText, int subTextStart) {
- CharSequence newSubText = Editable.Factory.getInstance().newEditable(subText);
- Objects.requireNonNull(newSubText);
+ Objects.requireNonNull(subText);
// Swap selection start and end if necessary.
final int subTextSelStart = initialSelStart > initialSelEnd
@@ -577,7 +583,7 @@
final int subTextSelEnd = initialSelStart > initialSelEnd
? initialSelStart - subTextStart : initialSelEnd - subTextStart;
- final int subTextLength = newSubText.length();
+ final int subTextLength = subText.length();
// Unknown or invalid selection.
if (subTextStart < 0 || subTextSelStart < 0 || subTextSelEnd > subTextLength) {
mInitialSurroundingText = new InitialSurroundingText();
@@ -591,12 +597,12 @@
}
if (subTextLength <= MEMORY_EFFICIENT_TEXT_LENGTH) {
- mInitialSurroundingText = new InitialSurroundingText(newSubText, subTextSelStart,
+ mInitialSurroundingText = new InitialSurroundingText(subText, subTextSelStart,
subTextSelEnd);
return;
}
- trimLongSurroundingText(newSubText, subTextSelStart, subTextSelEnd);
+ trimLongSurroundingText(subText, subTextSelStart, subTextSelEnd);
}
/**
@@ -901,7 +907,9 @@
InitialSurroundingText(@Nullable CharSequence surroundingText, int selectionHead,
int selectionEnd) {
- mSurroundingText = surroundingText;
+ // Copy the original text (without NoCopySpan) in case the original text is updated
+ // later.
+ mSurroundingText = copyWithParcelableSpans(surroundingText);
mSelectionHead = selectionHead;
mSelectionEnd = selectionEnd;
}
@@ -975,5 +983,30 @@
return new InitialSurroundingText[size];
}
};
+
+ /**
+ * Create a copy of the given {@link CharSequence} object, with completely copy
+ * {@link ParcelableSpan} instances.
+ *
+ * @param source the original {@link CharSequence} to be copied.
+ * @return the copied {@link CharSequence}. {@code null} if {@code source} is {@code null}.
+ */
+ @Nullable
+ private static CharSequence copyWithParcelableSpans(@Nullable CharSequence source) {
+ if (source == null) {
+ return null;
+ }
+ Parcel parcel = null;
+ try {
+ parcel = Parcel.obtain();
+ TextUtils.writeToParcel(source, parcel, /* parcelableFlags= */ 0);
+ parcel.setDataPosition(0);
+ return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+ } finally {
+ if (parcel != null) {
+ parcel.recycle();
+ }
+ }
+ }
}
}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 869a929..f44ab3a 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -382,7 +382,7 @@
/**
* Update token of the client window requesting {@link #showSoftInput(int, ResultReceiver)}
- * @param showInputToken dummy app window token for window requesting
+ * @param showInputToken placeholder app window token for window requesting
* {@link InputMethodManager#showSoftInput(View, int)}
* @hide
*/
@@ -390,7 +390,7 @@
/**
* Update token of the client window requesting {@link #hideSoftInput(int, ResultReceiver)}
- * @param hideInputToken dummy app window token for window requesting
+ * @param hideInputToken placeholder app window token for window requesting
* {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)}
* @hide
*/
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 11a6acf..67ed30f 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -1026,7 +1026,7 @@
}
/**
- * This is called when the user either releases his touch or the touch is
+ * This is called when the user either releases their touch or the touch is
* canceled.
*/
void onStopTrackingTouch() {
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index c1c1a6e..6aa4e46 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -483,7 +483,7 @@
/**
* Returns whether the spinners are shown.
* <p>
- * <strong>Note:</strong> his method returns {@code false} when the
+ * <strong>Note:</strong> this method returns {@code false} when the
* {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
* to {@code calendar}.
*
diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java
index bdfb550..51869d4 100644
--- a/core/java/android/widget/ExpandableListView.java
+++ b/core/java/android/widget/ExpandableListView.java
@@ -1007,7 +1007,7 @@
flatChildPos = mConnector.getFlattenedPos(elChildPos);
- // Sanity check
+ // Validity check
if (flatChildPos == null) {
throw new IllegalStateException("Could not find child");
}
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index b33660a..3bb39c1 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -123,7 +123,7 @@
/**
* True if the user is currently dragging this ScrollView around. This is
* not the same as 'is being flinged', which can be checked by
- * mScroller.isFinished() (flinging begins when the user lifts his finger).
+ * mScroller.isFinished() (flinging begins when the user lifts their finger).
*/
@UnsupportedAppUsage
private boolean mIsBeingDragged = false;
@@ -560,7 +560,7 @@
/*
* Shortcut the most recurring case: the user is in the dragging
- * state and he is moving his finger. We want to intercept this
+ * state and they are moving their finger. We want to intercept this
* motion.
*/
final int action = ev.getAction();
@@ -576,7 +576,7 @@
case MotionEvent.ACTION_MOVE: {
/*
* mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
- * whether the user has moved far enough from his original down touch.
+ * whether the user has moved far enough from their original down touch.
*/
/*
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 3b482a8..8dfb936 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -554,7 +554,7 @@
public static int SCROLL_STATE_IDLE = 0;
/**
- * The user is scrolling using touch, and his finger is still on the screen.
+ * The user is scrolling using touch, and their finger is still on the screen.
*/
public static int SCROLL_STATE_TOUCH_SCROLL = 1;
@@ -1675,7 +1675,7 @@
// Do not draw the middle item if input is visible since the input
// is shown only if the wheel is static and it covers the middle
// item. Otherwise, if the user starts editing the text via the
- // IME he may see a dimmed version of the old value intermixed
+ // IME they may see a dimmed version of the old value intermixed
// with the new one.
if ((showSelectorWheel && i != SELECTOR_MIDDLE_ITEM_INDEX) ||
(i == SELECTOR_MIDDLE_ITEM_INDEX && mInputText.getVisibility() != VISIBLE)) {
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index 849488d..4722fdc 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -389,7 +389,7 @@
/**
* <p>A pass-through listener acts upon the events and dispatches them
* to another listener. This allows the table layout to set its own internal
- * hierarchy change listener without preventing the user to setup his.</p>
+ * hierarchy change listener without preventing the user to setup this.</p>
*/
private class PassThroughHierarchyChangeListener implements
ViewGroup.OnHierarchyChangeListener {
@@ -529,4 +529,4 @@
private boolean isVisibleWithText(RadioButton button) {
return button.getVisibility() == VISIBLE && !TextUtils.isEmpty(button.getText());
}
-}
\ No newline at end of file
+}
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 3847d6b..b44b8c2 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -133,7 +133,7 @@
/**
* True if the user is currently dragging this ScrollView around. This is
* not the same as 'is being flinged', which can be checked by
- * mScroller.isFinished() (flinging begins when the user lifts his finger).
+ * mScroller.isFinished() (flinging begins when the user lifts their finger).
*/
@UnsupportedAppUsage
private boolean mIsBeingDragged = false;
@@ -593,7 +593,7 @@
/*
* Shortcut the most recurring case: the user is in the dragging
- * state and he is moving his finger. We want to intercept this
+ * state and they is moving their finger. We want to intercept this
* motion.
*/
final int action = ev.getAction();
@@ -616,7 +616,7 @@
case MotionEvent.ACTION_MOVE: {
/*
* mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
- * whether the user has moved far enough from his original down touch.
+ * whether the user has moved far enough from their original down touch.
*/
/*
diff --git a/core/java/android/widget/TableLayout.java b/core/java/android/widget/TableLayout.java
index 8bb4d16..8e635f4 100644
--- a/core/java/android/widget/TableLayout.java
+++ b/core/java/android/widget/TableLayout.java
@@ -751,7 +751,7 @@
/**
* <p>A pass-through listener acts upon the events and dispatches them
* to another listener. This allows the table layout to set its own internal
- * hierarchy change listener without preventing the user to setup his.</p>
+ * hierarchy change listener without preventing the user to setup this.</p>
*/
private class PassThroughHierarchyChangeListener implements
OnHierarchyChangeListener {
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index a3c29a8..491ddba 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -70,7 +70,7 @@
public class AccessibilityShortcutController {
private static final String TAG = "AccessibilityShortcutController";
- // Dummy component names for framework features
+ // Placeholder component names for framework features
public static final ComponentName COLOR_INVERSION_COMPONENT_NAME =
new ComponentName("com.android.server.accessibility", "ColorInversion");
public static final ComponentName DALTONIZER_COMPONENT_NAME =
@@ -109,8 +109,8 @@
public FrameworkObjectProvider mFrameworkObjectProvider = new FrameworkObjectProvider();
/**
- * @return An immutable map from dummy component names to feature info for toggling a framework
- * feature
+ * @return An immutable map from placeholder component names to feature
+ * info for toggling a framework feature
*/
public static Map<ComponentName, ToggleableFrameworkFeatureInfo>
getFrameworkShortcutFeaturesMap() {
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
index 508deac..0b92b93 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
@@ -42,7 +42,7 @@
/**
* Activity used to display various targets related to accessibility service, accessibility
- * activity or white listing feature for volume key shortcut.
+ * activity or allowlisting feature for volume key shortcut.
*/
public class AccessibilityShortcutChooserActivity extends Activity {
@ShortcutType
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
index d756593..d84175e 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
@@ -36,7 +36,7 @@
/**
* Abstract base class for creating various target related to accessibility service,
- * accessibility activity, and white listing feature.
+ * accessibility activity, and allowlisting feature.
*/
public abstract class AccessibilityTarget implements TargetOperations, OnTargetSelectedListener,
OnTargetCheckedChangeListener {
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
index 60a102a..a7c5f6d 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
@@ -101,7 +101,7 @@
/**
* Returns list of {@link AccessibilityTarget} of the installed accessibility service,
- * accessibility activity, and white listing feature including accessibility feature's package
+ * accessibility activity, and allowlisting feature including accessibility feature's package
* name, component id, etc.
*
* @param context The context of the application.
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index fd90b56..5910b33 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -3099,7 +3099,7 @@
final TargetInfo ti = mChooserMultiProfilePagerAdapter.getActiveListAdapter()
.targetInfoForPosition(mListPosition, /* filtered */ true);
- // This should always be the case for ItemViewHolder, check for sanity
+ // This should always be the case for ItemViewHolder, check for validity
if (ti instanceof DisplayResolveInfo) {
showTargetDetails((DisplayResolveInfo) ti);
}
@@ -3480,7 +3480,7 @@
v.setOnLongClickListener(v1 -> {
final TargetInfo ti = mChooserListAdapter.targetInfoForPosition(
holder.getItemIndex(column), true);
- // This should always be the case for non-DS targets, check for sanity
+ // This should always be the case for non-DS targets, check for validity
if (ti instanceof DisplayResolveInfo) {
showTargetDetails((DisplayResolveInfo) ti);
}
diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java
index 92e9fe4..56ec87c 100644
--- a/core/java/com/android/internal/app/NetInitiatedActivity.java
+++ b/core/java/com/android/internal/app/NetInitiatedActivity.java
@@ -35,7 +35,7 @@
import com.android.server.LocalServices;
/**
- * This activity is shown to the user for him/her to accept or deny network-initiated
+ * This activity is shown to the user for them to accept or deny network-initiated
* requests. It uses the alert dialog style. It will be launched from a notification.
*/
public class NetInitiatedActivity extends AlertActivity implements DialogInterface.OnClickListener {
diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java
index b3904f4..e2342bb 100644
--- a/core/java/com/android/internal/app/ToolbarActionBar.java
+++ b/core/java/com/android/internal/app/ToolbarActionBar.java
@@ -527,7 +527,7 @@
public View onCreatePanelView(int featureId) {
if (featureId == Window.FEATURE_OPTIONS_PANEL) {
// This gets called by PhoneWindow.preparePanel. Since this already manages
- // its own panel, we return a dummy view here to prevent PhoneWindow from
+ // its own panel, we return a placeholder view here to prevent PhoneWindow from
// preparing a default one.
return new View(mDecorToolbar.getContext());
}
diff --git a/core/java/com/android/internal/colorextraction/types/Tonal.java b/core/java/com/android/internal/colorextraction/types/Tonal.java
index d2e71c8..88bdb66 100644
--- a/core/java/com/android/internal/colorextraction/types/Tonal.java
+++ b/core/java/com/android/internal/colorextraction/types/Tonal.java
@@ -478,7 +478,7 @@
public ConfigParser(Context context) {
mTonalPalettes = new ArrayList<>();
- // Load all palettes and the blacklist from an XML.
+ // Load all palettes and the denylist from an XML.
try {
XmlPullParser parser = context.getResources().getXml(R.xml.color_extraction);
int eventType = parser.getEventType();
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 6314fcb..1ce246a 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -419,6 +419,17 @@
*/
public static final String SCREENSHOT_KEYCHORD_DELAY = "screenshot_keychord_delay";
+ /**
+ * (boolean) Whether to use an ML model for the Back Gesture.
+ */
+ public static final String USE_BACK_GESTURE_ML_MODEL = "use_back_gesture_ml_model";
+
+ /**
+ * (float) Threshold for Back Gesture ML model prediction.
+ */
+ public static final String BACK_GESTURE_ML_MODEL_THRESHOLD = "back_gesture_ml_model_threshold";
+
+
private SystemUiDeviceConfigFlags() {
}
}
diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java
index b2852ea..722e5c1 100644
--- a/core/java/com/android/internal/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -225,6 +225,7 @@
if (mService != null) {
mService.asBinder().unlinkToDeath(this, 0);
}
+ mBinding = false;
mService = null;
mServiceDied = true;
cancelScheduledUnbind();
diff --git a/core/java/com/android/internal/infra/GlobalWhitelistState.java b/core/java/com/android/internal/infra/GlobalWhitelistState.java
index a0b2f94..3c081e2 100644
--- a/core/java/com/android/internal/infra/GlobalWhitelistState.java
+++ b/core/java/com/android/internal/infra/GlobalWhitelistState.java
@@ -31,7 +31,7 @@
/**
* Helper class used to manage a {@link WhitelistHelper} per user instance when the main service
* cannot hold a lock when external entities (typically {@code ActivityManagerService}) needs to
- * get whitelist info.
+ * get allowlist info.
*
* <p>This class is thread safe.
*/
@@ -47,7 +47,7 @@
protected SparseArray<WhitelistHelper> mWhitelisterHelpers;
/**
- * Sets the whitelist for the given user.
+ * Sets the allowlist for the given user.
*/
public void setWhitelist(@UserIdInt int userId, @Nullable List<String> packageNames,
@Nullable List<ComponentName> components) {
@@ -65,7 +65,7 @@
}
/**
- * Checks if the given package is whitelisted for the given user.
+ * Checks if the given package is allowlisted for the given user.
*/
public boolean isWhitelisted(@UserIdInt int userId, @NonNull String packageName) {
synchronized (mGlobalWhitelistStateLock) {
@@ -76,7 +76,7 @@
}
/**
- * Checks if the given component is whitelisted for the given user.
+ * Checks if the given component is allowlisted for the given user.
*/
public boolean isWhitelisted(@UserIdInt int userId, @NonNull ComponentName componentName) {
synchronized (mGlobalWhitelistStateLock) {
@@ -87,7 +87,7 @@
}
/**
- * Gets the whitelisted components for the given package and user.
+ * Gets the allowlisted components for the given package and user.
*/
public ArraySet<ComponentName> getWhitelistedComponents(@UserIdInt int userId,
@NonNull String packageName) {
@@ -99,7 +99,7 @@
}
/**
- * Resets the whitelist for the given user.
+ * Resets the allowlist for the given user.
*/
public void resetWhitelist(@NonNull int userId) {
synchronized (mGlobalWhitelistStateLock) {
diff --git a/core/java/com/android/internal/infra/WhitelistHelper.java b/core/java/com/android/internal/infra/WhitelistHelper.java
index b1d85f7..1d76090 100644
--- a/core/java/com/android/internal/infra/WhitelistHelper.java
+++ b/core/java/com/android/internal/infra/WhitelistHelper.java
@@ -28,7 +28,7 @@
import java.util.Objects;
/**
- * Helper class for keeping track of whitelisted packages/activities.
+ * Helper class for keeping track of allowlisted packages/activities.
*
* <p><b>NOTE: </b>this class is not thread safe.
* @hide
@@ -38,18 +38,18 @@
private static final String TAG = "WhitelistHelper";
/**
- * Map of whitelisted packages/activities. The whole package is whitelisted if its
+ * Map of allowlisted packages/activities. The whole package is allowlisted if its
* corresponding value is {@code null}.
*/
@Nullable
private ArrayMap<String, ArraySet<ComponentName>> mWhitelistedPackages;
/**
- * Sets the whitelist with the given packages and activities. The list is cleared if both
+ * Sets the allowlist with the given packages and activities. The list is cleared if both
* packageNames and components are {@code null}.
*
- * @param packageNames packages to be whitelisted.
- * @param components activities to be whitelisted.
+ * @param packageNames packages to be allowlisted.
+ * @param components activities to be allowlisted.
*
* @throws IllegalArgumentException if packages or components are empty.
*/
@@ -103,7 +103,7 @@
}
/**
- * Returns {@code true} if the entire package is whitelisted.
+ * Returns {@code true} if the entire package is allowlisted.
*/
public boolean isWhitelisted(@NonNull String packageName) {
Objects.requireNonNull(packageName);
@@ -115,7 +115,7 @@
}
/**
- * Returns {@code true} if the specified activity is whitelisted.
+ * Returns {@code true} if the specified activity is allowlisted.
*/
public boolean isWhitelisted(@NonNull ComponentName componentName) {
Objects.requireNonNull(componentName);
@@ -130,8 +130,8 @@
}
/**
- * Returns a set of whitelisted components with the given package, or null if nothing is
- * whitelisted.
+ * Returns a set of allowlisted components with the given package, or null if nothing is
+ * allowlisted.
*/
@Nullable
public ArraySet<ComponentName> getWhitelistedComponents(@NonNull String packageName) {
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index e5475f8..9b1299e 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -371,7 +371,7 @@
/**
* Calls {@link IInputMethodPrivilegedOperations#applyImeVisibility(IBinder, boolean)}.
*
- * @param showOrHideInputToken dummy token that maps to window requesting
+ * @param showOrHideInputToken placeholder token that maps to window requesting
* {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} or
* {@link android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow
* (IBinder, int)}
diff --git a/core/java/com/android/internal/listeners/ListenerExecutor.java b/core/java/com/android/internal/listeners/ListenerExecutor.java
index 9979e60..7d9b9c9 100644
--- a/core/java/com/android/internal/listeners/ListenerExecutor.java
+++ b/core/java/com/android/internal/listeners/ListenerExecutor.java
@@ -38,71 +38,86 @@
void operate(TListener listener) throws Exception;
/**
- * Called before this operation is to be run. Some operations may be canceled before they
- * are run, in which case this method may not be called. {@link #onPostExecute(boolean)}
- * will only be run if this method was run. This callback is invoked on the calling thread.
+ * Called before this operation is to be run. An operation may be canceled before it is run,
+ * in which case this method may not be invoked. {@link #onPostExecute(boolean)} will only
+ * be invoked if this method was previously invoked. This callback is invoked on the
+ * calling thread.
*/
default void onPreExecute() {}
/**
* Called if the operation fails while running. Will not be invoked in the event of a
- * RuntimeException, which will propagate normally. Implementations of
- * {@link ListenerExecutor} have the option to override
- * {@link ListenerExecutor#onOperationFailure(ListenerOperation, Exception)} instead to
- * intercept failures at the class level. This callback is invoked on the executor thread.
+ * unchecked exception, which will propagate normally. This callback is invoked on the
+ * executor thread.
*/
- default void onFailure(Exception e) {
- // implementations should handle any exceptions that may be thrown
- throw new AssertionError(e);
- }
+ default void onFailure(Exception e) {}
/**
- * Called after the operation is run. This method will always be called if
- * {@link #onPreExecute()} is called. Success implies that the operation was run to
- * completion with no failures. This callback may be invoked on the calling thread or
- * executor thread.
+ * Called after the operation may have been run. Will always be invoked for every operation
+ * which has previously had {@link #onPreExecute()} invoked. Success implies that the
+ * operation was run to completion with no failures. If {@code success} is true, this
+ * callback will always be invoked on the executor thread. If {@code success} is false, this
+ * callback may be invoked on the calling thread or executor thread.
*/
default void onPostExecute(boolean success) {}
/**
- * Called after this operation is complete (which does not imply that it was necessarily
- * run). Will always be called once per operation, no matter if the operation was run or
- * not. Success implies that the operation was run to completion with no failures. This
- * callback may be invoked on the calling thread or executor thread.
+ * Will always be called once for every operation submitted to
+ * {@link #executeSafely(Executor, Supplier, ListenerOperation)}, no matter if the operation
+ * was run or not. This method is always invoked last, after every other callback. Success
+ * implies that the operation was run to completion with no failures. If {@code success}
+ * is true, this callback will always be invoked on the executor thread. If {@code success}
+ * is false, this callback may be invoked on the calling thread or executor thread.
*/
default void onComplete(boolean success) {}
}
/**
- * May be override to handle operation failures at a class level. Will not be invoked in the
- * event of a RuntimeException, which will propagate normally. This callback is invoked on the
- * executor thread.
+ * An callback for listener operation failure.
+ *
+ * @param <TListenerOperation> listener operation type
*/
- default <TListener> void onOperationFailure(ListenerOperation<TListener> operation,
- Exception exception) {
- operation.onFailure(exception);
+ interface FailureCallback<TListenerOperation extends ListenerOperation<?>> {
+
+ /**
+ * Called if a listener operation fails while running with a checked exception. This
+ * callback is invoked on the executor thread.
+ */
+ void onFailure(TListenerOperation operation, Exception exception);
+ }
+
+ /**
+ * See {@link #executeSafely(Executor, Supplier, ListenerOperation, FailureCallback)}.
+ */
+ default <TListener> void executeSafely(Executor executor, Supplier<TListener> listenerSupplier,
+ @Nullable ListenerOperation<TListener> operation) {
+ executeSafely(executor, listenerSupplier, operation, null);
}
/**
* Executes the given listener operation on the given executor, using the provided listener
* supplier. If the supplier returns a null value, or a value during the operation that does not
* match the value prior to the operation, then the operation is considered canceled. If a null
- * operation is supplied, nothing happens.
+ * operation is supplied, nothing happens. If a failure callback is supplied, this will be
+ * invoked on the executor thread in the event a checked exception is thrown from the listener
+ * operation.
*/
- default <TListener> void executeSafely(Executor executor, Supplier<TListener> listenerSupplier,
- @Nullable ListenerOperation<TListener> operation) {
+ default <TListener, TListenerOperation extends ListenerOperation<TListener>> void executeSafely(
+ Executor executor, Supplier<TListener> listenerSupplier,
+ @Nullable TListenerOperation operation,
+ @Nullable FailureCallback<TListenerOperation> failureCallback) {
if (operation == null) {
return;
}
+ TListener listener = listenerSupplier.get();
+ if (listener == null) {
+ return;
+ }
+
boolean executing = false;
boolean preexecute = false;
try {
- TListener listener = listenerSupplier.get();
- if (listener == null) {
- return;
- }
-
operation.onPreExecute();
preexecute = true;
executor.execute(() -> {
@@ -116,7 +131,10 @@
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
- onOperationFailure(operation, e);
+ operation.onFailure(e);
+ if (failureCallback != null) {
+ failureCallback.onFailure(operation, e);
+ }
}
} finally {
operation.onPostExecute(success);
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index dd1e4f4..095882e 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -305,7 +305,7 @@
/**
* Returns an HTTP user agent of the form
- * "Dalvik/1.1.0 (Linux; U; Android Eclair Build/MASTER)".
+ * "Dalvik/1.1.0 (Linux; U; Android Eclair Build/MAIN)".
*/
private static String getDefaultUserAgent() {
StringBuilder result = new StringBuilder(64);
@@ -324,7 +324,7 @@
result.append(model);
}
}
- String id = Build.ID; // "MASTER" or "M4-rc20"
+ String id = Build.ID; // "MAIN" or "M4-rc20"
if (id.length() > 0) {
result.append(" Build/");
result.append(id);
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 1ca9250..a985965 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -206,7 +206,7 @@
/** List of packages with the same uid, and its app data info: volume uuid and inode. */
public static final String PKG_DATA_INFO_MAP = "--pkg-data-info-map";
- /** List of whitelisted packages and its app data info: volume uuid and inode. */
+ /** List of allowlisted packages and its app data info: volume uuid and inode. */
public static final String WHITELISTED_DATA_INFO_MAP = "--whitelisted-data-info-map";
/** Bind mount app storage dirs to lower fs not via fuse */
@@ -327,7 +327,7 @@
* @param isTopApp true if the process is for top (high priority) application.
* @param pkgDataInfoList A list that stores related packages and its app data
* info: volume uuid and inode.
- * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for whitelisted apps.
+ * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for allowlisted apps.
* @param bindMountAppDataDirs True if the zygote needs to mount data dirs.
* @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs.
*
@@ -395,7 +395,7 @@
* volume uuid and CE dir inode. For example, pkgDataInfoList = [app_a_pkg_name,
* app_a_data_volume_uuid, app_a_ce_inode, app_b_pkg_name, app_b_data_volume_uuid,
* app_b_ce_inode, ...];
- * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for whitelisted apps.
+ * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for allowlisted apps.
* @param bindMountAppDataDirs True if the zygote needs to mount data dirs.
* @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs.
*/
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index 22082d0..ed07432 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -227,7 +227,7 @@
String[] mPkgDataInfoList;
/**
- * A list that stores all whitelisted app data info: volume uuid and inode.
+ * A list that stores all allowlisted app data info: volume uuid and inode.
* Null if it does need to do app data isolation.
*/
String[] mWhitelistedDataInfoList;
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index bb17337..a4ce027 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -297,7 +297,7 @@
mUsapPoolRefillDelayMs = Integer.parseInt(usapPoolRefillDelayMsPropString);
}
- // Sanity check
+ // Validity check
if (mUsapPoolSizeMin >= mUsapPoolSizeMax) {
Log.w(TAG, "The max size of the USAP pool must be greater than the minimum size."
+ " Restoring default values.");
diff --git a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
index 8a4eb4a..bed85ae 100644
--- a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
@@ -277,7 +277,6 @@
String group = groups[i];
IProtoLogGroup g = LOG_GROUPS.get(group);
if (g != null) {
- System.out.println("G: "+ g);
if (setTextLogging) {
g.setLogToLogcat(value);
} else {
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index ef2dfd5..cc2934f 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -22,6 +22,7 @@
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
import android.service.notification.StatusBarNotification;
import com.android.internal.statusbar.StatusBarIcon;
@@ -224,6 +225,11 @@
void stopTracing();
/**
+ * Handles a logging command from the WM shell command.
+ */
+ void handleWindowManagerLoggingCommand(in String[] args, in ParcelFileDescriptor outFd);
+
+ /**
* If true, suppresses the ambient display from showing. If false, re-enables the ambient
* display.
*/
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 25d1ae6..44dca9b 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -63,7 +63,7 @@
void onCallAttributesChanged(in CallAttributes callAttributes);
void onEmergencyNumberListChanged(in Map emergencyNumberList);
void onOutgoingEmergencyCall(in EmergencyNumber placedEmergencyNumber, int subscriptionId);
- void onOutgoingEmergencySms(in EmergencyNumber sentEmergencyNumber);
+ void onOutgoingEmergencySms(in EmergencyNumber sentEmergencyNumber, int subscriptionId);
void onCallDisconnectCauseChanged(in int disconnectCause, in int preciseDisconnectCause);
void onImsCallDisconnectCauseChanged(in ImsReasonInfo imsReasonInfo);
void onRegistrationFailed(in CellIdentity cellIdentity,
diff --git a/core/java/com/android/internal/view/menu/MenuPopup.java b/core/java/com/android/internal/view/menu/MenuPopup.java
index 10bd66f..fb871a4 100644
--- a/core/java/com/android/internal/view/menu/MenuPopup.java
+++ b/core/java/com/android/internal/view/menu/MenuPopup.java
@@ -136,7 +136,7 @@
*/
protected static int measureIndividualMenuWidth(ListAdapter adapter, ViewGroup parent,
Context context, int maxAllowedWidth) {
- // Menus don't tend to be long, so this is more sane than it looks.
+ // Menus don't tend to be long, so this is more valid than it looks.
int maxWidth = 0;
View itemView = null;
int itemType = 0;
diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java
index baf3188..eef3368 100644
--- a/core/java/com/android/internal/widget/ActionBarContainer.java
+++ b/core/java/com/android/internal/widget/ActionBarContainer.java
@@ -361,7 +361,7 @@
}
/**
- * Dummy drawable so that we don't break background display lists and
+ * Placeholder drawable so that we don't break background display lists and
* projection surfaces.
*/
private class ActionBarBackgroundDrawable extends Drawable {
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 08a9f48..c0c7f10 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1211,7 +1211,7 @@
}
/**
- * Set and store the lockout deadline, meaning the user can't attempt his/her unlock
+ * Set and store the lockout deadline, meaning the user can't attempt their unlock
* pattern until the deadline has passed.
* @return the chosen deadline.
*/
@@ -1229,7 +1229,7 @@
/**
* @return The elapsed time in millis in the future when the user is allowed to
- * attempt to enter his/her lock pattern, or 0 if the user is welcome to
+ * attempt to enter their lock pattern, or 0 if the user is welcome to
* enter a pattern.
*/
public long getLockoutAttemptDeadline(int userId) {
@@ -1515,7 +1515,7 @@
* Create an escrow token for the current user, which can later be used to unlock FBE
* or change user password.
*
- * After adding, if the user currently has lockscreen password, he will need to perform a
+ * After adding, if the user currently has lockscreen password, they will need to perform a
* confirm credential operation in order to activate the token for future use. If the user
* has no secure lockscreen, then the token is activated immediately.
*
@@ -1592,7 +1592,7 @@
/**
* Unlock the specified user by an pre-activated escrow token. This should have the same effect
- * on device encryption as the user entering his lockscreen credentials for the first time after
+ * on device encryption as the user entering their lockscreen credentials for the first time after
* boot, this includes unlocking the user's credential-encrypted storage as well as the keystore
*
* <p>This method is only available to code running in the system server process itself.
diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java
index 38588ea..f5df3ab 100644
--- a/core/java/com/android/internal/widget/LockSettingsInternal.java
+++ b/core/java/com/android/internal/widget/LockSettingsInternal.java
@@ -30,7 +30,7 @@
* Create an escrow token for the current user, which can later be used to unlock FBE
* or change user password.
*
- * After adding, if the user currently has lockscreen password, he will need to perform a
+ * After adding, if the user currently has lockscreen password, they will need to perform a
* confirm credential operation in order to activate the token for future use.
* Once the token is activated, the callback that is passed here is called. If the user
* has no secure lockscreen, then the token is activated immediately.
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index c8a86d1..6f377b9 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -1843,7 +1843,7 @@
case MotionEvent.ACTION_MOVE: {
/*
* mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
- * whether the user has moved far enough from his original down touch.
+ * whether the user has moved far enough from their original down touch.
*/
/*
diff --git a/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
index b4610bd..ce9ab82 100644
--- a/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
+++ b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java
@@ -262,14 +262,14 @@
boolean currentMasterSyncEnabled = ContentResolver.getMasterSyncAutomaticallyAsUser(
mUserId);
if (currentMasterSyncEnabled) {
- // Disable master sync to prevent any syncs from running.
+ // Disable global sync to prevent any syncs from running.
ContentResolver.setMasterSyncAutomaticallyAsUser(false, mUserId);
}
try {
restoreFromJsonArray(accountJSONArray, mUserId);
} finally {
- // Set the master sync preference to the value from the backup set.
+ // Set the global sync preference to the value from the backup set.
ContentResolver.setMasterSyncAutomaticallyAsUser(masterSyncEnabled, mUserId);
}
Log.i(TAG, "Restore successful.");
diff --git a/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java b/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java
index ffae757..e1fe1bd 100644
--- a/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java
+++ b/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java
@@ -113,7 +113,7 @@
* <li>
* <p>
* Send the certificate request to the trusted Certificate Authority for signature.
- * One may choose to act as her own CA and sign the certificate request using a PKI
+ * One may choose to act as their own CA and sign the certificate request using a PKI
* tool, such as OpenSSL.
* </p>
* </li>
diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp
index 77c1a10..4e50f87 100644
--- a/core/jni/LayoutlibLoader.cpp
+++ b/core/jni/LayoutlibLoader.cpp
@@ -17,9 +17,9 @@
#include "jni.h"
#include "core_jni_helpers.h"
-#include <sstream>
-#include <iostream>
#include <unicode/putil.h>
+#include <clocale>
+#include <sstream>
#include <unordered_map>
#include <vector>
@@ -198,6 +198,8 @@
u_setDataDirectory(path);
env->ReleaseStringUTFChars(stringPath, path);
+ // Use English locale for number format to ensure correct parsing of floats when using strtof
+ setlocale(LC_NUMERIC, "en_US.UTF-8");
return JNI_VERSION_1_6;
}
diff --git a/core/jni/android_hardware_HardwareBuffer.cpp b/core/jni/android_hardware_HardwareBuffer.cpp
index e78e08e..2944f72 100644
--- a/core/jni/android_hardware_HardwareBuffer.cpp
+++ b/core/jni/android_hardware_HardwareBuffer.cpp
@@ -30,8 +30,9 @@
#include <binder/Parcel.h>
-#include <ui/GraphicBuffer.h>
#include <private/gui/ComposerService.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/PixelFormat.h>
#include <hardware/gralloc1.h>
#include <grallocusage/GrallocUsageConversion.h>
@@ -166,6 +167,20 @@
return AHardwareBuffer_convertFromGrallocUsageBits(buffer->getUsage());
}
+static jlong android_hardware_HardwareBuffer_estimateSize(jlong nativeObject) {
+ GraphicBuffer* buffer = GraphicBufferWrapper_to_GraphicBuffer(nativeObject);
+
+ uint32_t bpp = bytesPerPixel(buffer->getPixelFormat());
+ if (bpp == 0) {
+ // If the pixel format is not recognized, use 1 as default.
+ bpp = 1;
+ }
+
+ const uint32_t bufferStride =
+ buffer->getStride() > 0 ? buffer->getStride() : buffer->getWidth();
+ return static_cast<jlong>(buffer->getHeight() * bufferStride * bpp);
+}
+
// ----------------------------------------------------------------------------
// Serialization
// ----------------------------------------------------------------------------
@@ -247,6 +262,7 @@
const char* const kClassPathName = "android/hardware/HardwareBuffer";
+// clang-format off
static const JNINativeMethod gMethods[] = {
{ "nCreateHardwareBuffer", "(IIIIJ)J",
(void*) android_hardware_HardwareBuffer_create },
@@ -267,7 +283,11 @@
{ "nGetFormat", "(J)I", (void*) android_hardware_HardwareBuffer_getFormat },
{ "nGetLayers", "(J)I", (void*) android_hardware_HardwareBuffer_getLayers },
{ "nGetUsage", "(J)J", (void*) android_hardware_HardwareBuffer_getUsage },
+
+ // --------------- @CriticalNative ----------------------
+ { "nEstimateSize", "(J)J", (void*) android_hardware_HardwareBuffer_estimateSize },
};
+// clang-format on
int register_android_hardware_HardwareBuffer(JNIEnv* env) {
int err = RegisterMethodsOrDie(env, kClassPathName, gMethods,
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 7cfe3bc..0892b70 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -114,7 +114,7 @@
return parcel ? parcel->dataCapacity() : 0;
}
-static jlong android_os_Parcel_setDataSize(JNIEnv* env, jclass clazz, jlong nativePtr, jint size)
+static void android_os_Parcel_setDataSize(JNIEnv* env, jclass clazz, jlong nativePtr, jint size)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
@@ -122,9 +122,7 @@
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
- return parcel->getOpenAshmemSize();
}
- return 0;
}
static void android_os_Parcel_setDataPosition(jlong nativePtr, jint pos)
@@ -308,7 +306,7 @@
}
}
-static jlong android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
+static void android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
@@ -317,9 +315,7 @@
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
- return parcel->getOpenAshmemSize();
}
- return 0;
}
static jbyteArray android_os_Parcel_createByteArray(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -506,14 +502,12 @@
return reinterpret_cast<jlong>(parcel);
}
-static jlong android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr)
+static void android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
parcel->freeData();
- return parcel->getOpenAshmemSize();
}
- return 0;
}
static void android_os_Parcel_destroy(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -551,12 +545,12 @@
return ret;
}
-static jlong android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr,
+static void android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr,
jbyteArray data, jint offset, jint length)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel == NULL || length < 0) {
- return 0;
+ return;
}
jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(data, 0);
@@ -570,7 +564,6 @@
env->ReleasePrimitiveArrayCritical(data, array, 0);
}
- return parcel->getOpenAshmemSize();
}
static jint android_os_Parcel_compareData(JNIEnv* env, jclass clazz, jlong thisNativePtr,
@@ -588,23 +581,23 @@
return thisParcel->compareData(*otherParcel);
}
-static jlong android_os_Parcel_appendFrom(JNIEnv* env, jclass clazz, jlong thisNativePtr,
+static void android_os_Parcel_appendFrom(JNIEnv* env, jclass clazz, jlong thisNativePtr,
jlong otherNativePtr, jint offset, jint length)
{
Parcel* thisParcel = reinterpret_cast<Parcel*>(thisNativePtr);
if (thisParcel == NULL) {
- return 0;
+ return;
}
Parcel* otherParcel = reinterpret_cast<Parcel*>(otherNativePtr);
if (otherParcel == NULL) {
- return thisParcel->getOpenAshmemSize();
+ return;
}
status_t err = thisParcel->appendFrom(otherParcel, offset, length);
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
- return thisParcel->getOpenAshmemSize();
+ return;
}
static jboolean android_os_Parcel_hasFileDescriptors(jlong nativePtr)
@@ -720,7 +713,7 @@
// @CriticalNative
{"nativeDataCapacity", "(J)I", (void*)android_os_Parcel_dataCapacity},
// @FastNative
- {"nativeSetDataSize", "(JI)J", (void*)android_os_Parcel_setDataSize},
+ {"nativeSetDataSize", "(JI)V", (void*)android_os_Parcel_setDataSize},
// @CriticalNative
{"nativeSetDataPosition", "(JI)V", (void*)android_os_Parcel_setDataPosition},
// @FastNative
@@ -749,7 +742,7 @@
// @FastNative
{"nativeWriteStrongBinder", "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder},
// @FastNative
- {"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)J", (void*)android_os_Parcel_writeFileDescriptor},
+ {"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)V", (void*)android_os_Parcel_writeFileDescriptor},
{"nativeCreateByteArray", "(J)[B", (void*)android_os_Parcel_createByteArray},
{"nativeReadByteArray", "(J[BI)Z", (void*)android_os_Parcel_readByteArray},
@@ -772,13 +765,13 @@
{"nativeReadFileDescriptor", "(J)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_readFileDescriptor},
{"nativeCreate", "()J", (void*)android_os_Parcel_create},
- {"nativeFreeBuffer", "(J)J", (void*)android_os_Parcel_freeBuffer},
+ {"nativeFreeBuffer", "(J)V", (void*)android_os_Parcel_freeBuffer},
{"nativeDestroy", "(J)V", (void*)android_os_Parcel_destroy},
{"nativeMarshall", "(J)[B", (void*)android_os_Parcel_marshall},
- {"nativeUnmarshall", "(J[BII)J", (void*)android_os_Parcel_unmarshall},
+ {"nativeUnmarshall", "(J[BII)V", (void*)android_os_Parcel_unmarshall},
{"nativeCompareData", "(JJ)I", (void*)android_os_Parcel_compareData},
- {"nativeAppendFrom", "(JJII)J", (void*)android_os_Parcel_appendFrom},
+ {"nativeAppendFrom", "(JJII)V", (void*)android_os_Parcel_appendFrom},
// @CriticalNative
{"nativeHasFileDescriptors", "(J)Z", (void*)android_os_Parcel_hasFileDescriptors},
{"nativeWriteInterfaceToken", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeInterfaceToken},
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 50a557bb..3acd15a 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -59,7 +59,8 @@
jobject mReceiverWeakGlobal;
sp<MessageQueue> mMessageQueue;
- void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
+ void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
+ int64_t sharedTimelineFrameCount) override;
void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
int32_t configId, nsecs_t vsyncPeriod) override;
@@ -90,14 +91,14 @@
}
void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId,
- uint32_t count) {
+ uint32_t count, int64_t frameTimelineVsyncId) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
if (receiverObj.get()) {
ALOGV("receiver %p ~ Invoking vsync handler.", this);
env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync,
- timestamp, displayId.value, count);
+ timestamp, displayId.value, count, frameTimelineVsyncId);
ALOGV("receiver %p ~ Returned from vsync handler.", this);
}
@@ -196,8 +197,8 @@
jclass clazz = FindClassOrDie(env, "android/view/DisplayEventReceiver");
gDisplayEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
- gDisplayEventReceiverClassInfo.dispatchVsync = GetMethodIDOrDie(env,
- gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJI)V");
+ gDisplayEventReceiverClassInfo.dispatchVsync =
+ GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJIJ)V");
gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
gDisplayEventReceiverClassInfo.dispatchConfigChanged = GetMethodIDOrDie(env,
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 4ef15d7..9efe4b1 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -30,6 +30,7 @@
#include <android_runtime/android_hardware_HardwareBuffer.h>
#include <android_runtime/android_view_Surface.h>
#include <android_runtime/android_view_SurfaceSession.h>
+#include <gui/IScreenCaptureListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
@@ -188,6 +189,11 @@
static struct {
jclass clazz;
+ jmethodID onScreenCaptureComplete;
+} gScreenCaptureListenerClassInfo;
+
+static struct {
+ jclass clazz;
jmethodID ctor;
jfieldID defaultConfig;
jfieldID primaryRefreshRateMin;
@@ -226,6 +232,56 @@
}
}
+class ScreenCaptureListenerWrapper : public BnScreenCaptureListener {
+public:
+ explicit ScreenCaptureListenerWrapper(JNIEnv* env, jobject jobject) {
+ env->GetJavaVM(&mVm);
+ screenCaptureListenerObject = env->NewGlobalRef(jobject);
+ LOG_ALWAYS_FATAL_IF(!screenCaptureListenerObject, "Failed to make global ref");
+ }
+
+ ~ScreenCaptureListenerWrapper() {
+ if (screenCaptureListenerObject) {
+ getenv()->DeleteGlobalRef(screenCaptureListenerObject);
+ screenCaptureListenerObject = nullptr;
+ }
+ }
+
+ status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override {
+ JNIEnv* env = getenv();
+ if (captureResults.result != NO_ERROR || captureResults.buffer == nullptr) {
+ env->CallVoidMethod(screenCaptureListenerObject,
+ gScreenCaptureListenerClassInfo.onScreenCaptureComplete, nullptr);
+ return NO_ERROR;
+ }
+ jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
+ env, captureResults.buffer->toAHardwareBuffer());
+ const jint namedColorSpace =
+ fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace);
+ jobject screenshotHardwareBuffer =
+ env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
+ gScreenshotHardwareBufferClassInfo.builder,
+ jhardwareBuffer, namedColorSpace,
+ captureResults.capturedSecureLayers);
+ env->CallVoidMethod(screenCaptureListenerObject,
+ gScreenCaptureListenerClassInfo.onScreenCaptureComplete,
+ screenshotHardwareBuffer);
+ env->DeleteLocalRef(jhardwareBuffer);
+ env->DeleteLocalRef(screenshotHardwareBuffer);
+ return NO_ERROR;
+ }
+
+private:
+ jobject screenCaptureListenerObject;
+ JavaVM* mVm;
+
+ JNIEnv* getenv() {
+ JNIEnv* env;
+ mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
+ return env;
+ }
+};
+
// ----------------------------------------------------------------------------
static jlong nativeCreateTransaction(JNIEnv* env, jclass clazz) {
@@ -327,36 +383,28 @@
return captureArgs;
}
-static jobject nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject) {
+static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject,
+ jobject screenCaptureListenerObject) {
const DisplayCaptureArgs captureArgs =
displayCaptureArgsFromObject(env, displayCaptureArgsObject);
if (captureArgs.displayToken == NULL) {
- return NULL;
+ return BAD_VALUE;
}
- ScreenCaptureResults captureResults;
- status_t res = ScreenshotClient::captureDisplay(captureArgs, captureResults);
- if (res != NO_ERROR) {
- return NULL;
- }
-
- jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
- env, captureResults.buffer->toAHardwareBuffer());
- const jint namedColorSpace =
- fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace);
- return env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
- gScreenshotHardwareBufferClassInfo.builder, jhardwareBuffer,
- namedColorSpace, captureResults.capturedSecureLayers);
+ sp<IScreenCaptureListener> captureListener =
+ new ScreenCaptureListenerWrapper(env, screenCaptureListenerObject);
+ return ScreenshotClient::captureDisplay(captureArgs, captureListener);
}
-static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject) {
+static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject,
+ jobject screenCaptureListenerObject) {
LayerCaptureArgs captureArgs;
getCaptureArgs(env, layerCaptureArgsObject, captureArgs);
SurfaceControl* layer = reinterpret_cast<SurfaceControl*>(
env->GetLongField(layerCaptureArgsObject, gLayerCaptureArgsClassInfo.layer));
if (layer == nullptr) {
- return nullptr;
+ return BAD_VALUE;
}
captureArgs.layerHandle = layer->getHandle();
@@ -380,19 +428,9 @@
env->ReleaseLongArrayElements(excludeObjectArray, const_cast<jlong*>(objects), JNI_ABORT);
}
- ScreenCaptureResults captureResults;
- status_t res = ScreenshotClient::captureLayers(captureArgs, captureResults);
- if (res != NO_ERROR) {
- return NULL;
- }
-
- jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
- env, captureResults.buffer->toAHardwareBuffer());
- const jint namedColorSpace =
- fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace);
- return env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
- gScreenshotHardwareBufferClassInfo.builder, jhardwareBuffer,
- namedColorSpace, captureResults.capturedSecureLayers);
+ sp<IScreenCaptureListener> captureListener =
+ new ScreenCaptureListenerWrapper(env, screenCaptureListenerObject);
+ return ScreenshotClient::captureLayers(captureArgs, captureListener);
}
static void nativeApplyTransaction(JNIEnv* env, jclass clazz, jlong transactionObj, jboolean sync) {
@@ -1664,12 +1702,10 @@
{"nativeSetOverrideScalingMode", "(JJI)V",
(void*)nativeSetOverrideScalingMode },
{"nativeCaptureDisplay",
- "(Landroid/view/SurfaceControl$DisplayCaptureArgs;)"
- "Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;",
+ "(Landroid/view/SurfaceControl$DisplayCaptureArgs;Landroid/view/SurfaceControl$ScreenCaptureListener;)I",
(void*)nativeCaptureDisplay },
{"nativeCaptureLayers",
- "(Landroid/view/SurfaceControl$LayerCaptureArgs;)"
- "Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;",
+ "(Landroid/view/SurfaceControl$LayerCaptureArgs;Landroid/view/SurfaceControl$ScreenCaptureListener;)I",
(void*)nativeCaptureLayers },
{"nativeSetInputWindowInfo", "(JJLandroid/view/InputWindowHandle;)V",
(void*)nativeSetInputWindowInfo },
@@ -1875,6 +1911,12 @@
gLayerCaptureArgsClassInfo.childrenOnly =
GetFieldIDOrDie(env, layerCaptureArgsClazz, "mChildrenOnly", "Z");
+ jclass screenCaptureListenerClazz =
+ FindClassOrDie(env, "android/view/SurfaceControl$ScreenCaptureListener");
+ gScreenCaptureListenerClassInfo.clazz = MakeGlobalRefOrDie(env, screenCaptureListenerClazz);
+ gScreenCaptureListenerClassInfo.onScreenCaptureComplete =
+ GetMethodIDOrDie(env, screenCaptureListenerClazz, "onScreenCaptureComplete",
+ "(Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;)V");
return err;
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index f96ed36..e6bfecc 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -826,7 +826,7 @@
}
if (mount_mode == MOUNT_EXTERNAL_NONE && !force_mount_namespace) {
- // Sane default of no storage visible
+ // Valid default of no storage visible
return;
}
@@ -1108,7 +1108,7 @@
// Temporarily block SIGCHLD during forks. The SIGCHLD handler might
// log, which would result in the logging FDs we close being reopened.
- // This would cause failures because the FDs are not whitelisted.
+ // This would cause failures because the FDs are not allowlisted.
//
// Note that the zygote process is single threaded at this point.
BlockSignal(SIGCHLD, fail_fn);
@@ -1314,7 +1314,7 @@
* in data directories.
*
* Steps:
- * 1). Collect a list of all related apps (apps with same uid and whitelisted apps) data info
+ * 1). Collect a list of all related apps (apps with same uid and allowlisted apps) data info
* (package name, data stored volume uuid, and inode number of its CE data directory)
* 2). Mount tmpfs on /data/data, /data/user(_de) and /mnt/expand, so apps no longer
* able to access apps data directly.
@@ -2048,7 +2048,7 @@
return;
}
- // Mark rootfs as being a slave so that changes from default
+ // Mark rootfs as being MS_SLAVE so that changes from default
// namespace only flow into our children.
if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
RuntimeAbort(env, __LINE__, "Failed to mount() rootfs as MS_SLAVE");
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 5609b36..9fed1b9 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -40,6 +40,7 @@
import "frameworks/base/core/proto/android/server/jobscheduler.proto";
import "frameworks/base/core/proto/android/server/location/context_hub.proto";
import "frameworks/base/core/proto/android/server/powermanagerservice.proto";
+import "frameworks/base/core/proto/android/server/powerstatsservice.proto";
import "frameworks/base/core/proto/android/server/rolemanagerservice.proto";
import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
import "frameworks/base/core/proto/android/service/appwidget.proto";
@@ -510,6 +511,11 @@
(section).args = "sensorservice --proto"
];
+ optional com.android.server.powerstats.PowerStatsServiceProto powerstats = 3054 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "power_stats --proto"
+ ];
+
// Dumps in text format (on userdebug and eng builds only): 4000 ~ 4999
optional android.util.TextDumpProto textdump_wifi = 4000 [
(section).type = SECTION_TEXT_DUMPSYS,
diff --git a/core/proto/android/server/powerstatsservice.proto b/core/proto/android/server/powerstatsservice.proto
new file mode 100644
index 0000000..c805244
--- /dev/null
+++ b/core/proto/android/server/powerstatsservice.proto
@@ -0,0 +1,67 @@
+/*
+ * 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 com.android.server.powerstats;
+
+option java_multiple_files = true;
+
+message IncidentReportProto {
+ /** Section number matches that in incident.proto */
+ optional PowerStatsServiceProto incident_report = 3054;
+}
+
+message PowerStatsServiceProto {
+ repeated RailInfoProto rail_info = 1;
+ repeated EnergyDataProto energy_data = 2;
+}
+
+/**
+ * Rail information:
+ * Reports information related to the rails being monitored.
+ */
+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;
+}
+
+/**
+ * Rail level energy measurements:
+ * Reports accumulated energy since boot on each rail.
+ */
+message EnergyDataProto {
+ /**
+ * Index corresponding to the rail. This index matches
+ * the index returned in RailInfo
+ */
+ optional int32 index = 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;
+}
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 3a59a16..d4b226d 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -316,7 +316,7 @@
optional bool front_of_task = 28;
optional int32 proc_id = 29;
optional bool translucent = 30;
- optional bool pip_auto_enter_allowed = 31;
+ optional bool pip_auto_enter_enabled = 31;
}
/* represents WindowToken */
diff --git a/core/proto/android/stats/hdmi/enums.proto b/core/proto/android/stats/hdmi/enums.proto
new file mode 100644
index 0000000..acb8899
--- /dev/null
+++ b/core/proto/android/stats/hdmi/enums.proto
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.stats.hdmi;
+option java_multiple_files = true;
+option java_outer_classname = "HdmiStatsEnums";
+
+// HDMI CEC logical addresses.
+// Values correspond to "CEC Table 5 Logical Addresses" in the HDMI CEC 1.4b spec.
+enum LogicalAddress {
+ LOGICAL_ADDRESS_UNKNOWN = -1;
+ TV = 0;
+ RECORDING_DEVICE_1 = 1;
+ RECORDING_DEVICE_2 = 2;
+ TUNER_1 = 3;
+ PLAYBACK_DEVICE_1 = 4;
+ AUDIO_SYSTEM = 5;
+ TUNER_2 = 6;
+ TUNER_3 = 7;
+ PLAYBACK_DEVICE_2 = 8;
+ RECORDING_DEVICE_3 = 9;
+ TUNER_4 = 10;
+ PLAYBACK_DEVICE_3 = 11;
+ RESERVED_1 = 12;
+ RESERVED_2 = 13;
+ SPECIFIC_USE = 14;
+ UNREGISTERED_OR_BROADCAST = 15;
+}
+
+// The relationship between two paths.
+// Values correspond exactly to PathRelationship in com.android.server.hdmi.Constants.
+enum PathRelationship {
+ RELATIONSHIP_TO_ACTIVE_SOURCE_UNKNOWN = 0;
+ DIFFERENT_BRANCH = 1;
+ ANCESTOR = 2;
+ DESCENDANT = 3;
+ SIBLING = 4;
+ SAME = 5;
+}
+
+// The result of attempting to send a HDMI CEC message.
+// Values correspond to the constants in android.hardware.tv.cec.V1_0.SendMessageResult,
+// offset by 10.
+enum SendMessageResult {
+ SEND_MESSAGE_RESULT_UNKNOWN = 0;
+ SUCCESS = 10;
+ NACK = 11;
+ BUSY = 12;
+ FAIL = 13;
+}
+
+// Whether a HDMI CEC message is sent from this device, to this device, or neither.
+enum MessageDirection {
+ MESSAGE_DIRECTION_UNKNOWN = 0;
+ MESSAGE_DIRECTION_OTHER = 1; // None of the other options.
+ OUTGOING = 2; // Sent from this device.
+ INCOMING = 3; // Sent to this device.
+ TO_SELF = 4; // Sent from this device, to this device. Indicates a bug.
+}
+
+// User control commands. Each value can represent an individual command, or a set of commands.
+// Values correspond to "CEC Table 30 UI Command Codes" in the HDMI CEC 1.4b spec, offset by 0x100.
+enum UserControlPressedCommand {
+ USER_CONTROL_PRESSED_COMMAND_UNKNOWN = 0;
+
+ // Represents all codes that are not represented by another value.
+ USER_CONTROL_PRESSED_COMMAND_OTHER = 1;
+
+ // Represents all number codes (codes 0x1E through 0x29).
+ NUMBER = 2;
+
+ // Navigation
+ SELECT = 0x100;
+ UP = 0x101;
+ DOWN = 0x102;
+ LEFT = 0x103;
+ RIGHT = 0x104;
+ RIGHT_UP = 0x105;
+ RIGHT_DOWN = 0x106;
+ LEFT_UP = 0x107;
+ LEFT_DOWN = 0x108;
+ EXIT = 0x10D;
+
+ // Volume
+ VOLUME_UP = 0x141;
+ VOLUME_DOWN = 0x142;
+ VOLUME_MUTE = 0x143;
+
+ // Power
+ POWER = 0x140;
+ POWER_TOGGLE = 0x16B;
+ POWER_OFF = 0x16C;
+ POWER_ON = 0x16D;
+}
+
+// Reason parameter of the <Feature Abort> message.
+// Values correspond to "CEC Table 29 Operand Descriptions" in the HDMI CEC 1.4b spec,
+// offset by 10.
+enum FeatureAbortReason {
+ FEATURE_ABORT_REASON_UNKNOWN = 0;
+ UNRECOGNIZED_OPCODE = 10;
+ NOT_IN_CORRECT_MODE_TO_RESPOND = 11;
+ CANNOT_PROVIDE_SOURCE = 12;
+ INVALID_OPERAND = 13;
+ REFUSED = 14;
+ UNABLE_TO_DETERMINE = 15;
+}
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 560e3c1..c9a4ab0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -107,6 +107,8 @@
<!-- @deprecated This is rarely used and will be phased out soon. -->
<protected-broadcast android:name="android.os.action.SCREEN_BRIGHTNESS_BOOST_CHANGED" />
+ <protected-broadcast android:name="android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL" />
+
<protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />
<protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" />
<protected-broadcast android:name="android.app.action.ENTER_CAR_MODE_PRIORITIZED" />
@@ -236,6 +238,8 @@
android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY" />
<protected-broadcast
android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.action.TETHERING_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED" />
@@ -892,11 +896,11 @@
<p> This is a soft restricted permission which cannot be held by an app it its
full form until the installer on record whitelists the permission.
- Specifically, if the permission is whitelisted the holder app can access
+ Specifically, if the permission is allowlisted the holder app can access
external storage and the visual and aural media collections while if the
- permission is not whitelisted the holder app can only access to the visual
+ permission is not allowlisted the holder app can only access to the visual
and aural medial collections. Also the permission is immutably restricted
- meaning that the whitelist state can be specified only at install time and
+ meaning that the allowlist state can be specified only at install time and
cannot change until the app is installed. For more details see
{@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
<p>Protection level: dangerous -->
@@ -920,7 +924,7 @@
read/write files in your application-specific directories returned by
{@link android.content.Context#getExternalFilesDir} and
{@link android.content.Context#getExternalCacheDir}.
- <p>If this permission is not whitelisted for an app that targets an API level before
+ <p>If this permission is not allowlisted for an app that targets an API level before
{@link android.os.Build.VERSION_CODES#Q} this permission cannot be granted to apps.</p>
<p>Protection level: dangerous</p>
-->
@@ -3781,7 +3785,7 @@
<permission android:name="android.permission.UPGRADE_RUNTIME_PERMISSIONS"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows an application to whitelist restricted permissions
+ <!-- @SystemApi Allows an application to allowlist restricted permissions
on any of the whitelists.
@hide -->
<permission android:name="android.permission.WHITELIST_RESTRICTED_PERMISSIONS"
@@ -4155,7 +4159,7 @@
<permission android:name="android.permission.CHANGE_APP_IDLE_STATE"
android:protectionLevel="signature|privileged" />
- <!-- @hide @SystemApi Allows an application to temporarily whitelist an inactive app to
+ <!-- @hide @SystemApi Allows an application to temporarily allowlist an inactive app to
access the network and acquire wakelocks.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"
@@ -4779,7 +4783,7 @@
<permission android:name="android.permission.ACCESS_VR_STATE"
android:protectionLevel="signature|preinstalled" />
- <!-- Allows an application to whitelist tasks during lock task mode
+ <!-- Allows an application to allowlist tasks during lock task mode
@hide <p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.UPDATE_LOCK_TASK_PACKAGES"
android:protectionLevel="signature|setup" />
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index d0d7eca..f07fea7 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -400,7 +400,7 @@
<string name="permlab_getPackageSize" msgid="375391550792886641">"قياس مساحة تخزين التطبيق"</string>
<string name="permdesc_getPackageSize" msgid="742743530909966782">"للسماح للتطبيق باسترداد شفرته وبياناته وأحجام ذاكرات التخزين المؤقت"</string>
<string name="permlab_writeSettings" msgid="8057285063719277394">"تعديل إعدادات النظام"</string>
- <string name="permdesc_writeSettings" msgid="8293047411196067188">"للسماح للتطبيق بتعديل بيانات إعدادات النظام. يمكن أن تتلف التطبيقات الضارة تهيئة نظامك."</string>
+ <string name="permdesc_writeSettings" msgid="8293047411196067188">"للسماح للتطبيق بتعديل بيانات إعدادات النظام. يمكن أن تتلف التطبيقات الضارة إعداد نظامك."</string>
<string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"العمل عند بدء التشغيل"</string>
<string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"للسماح للتطبيق ببدء تشغيل نفسه عقب انتهاء النظام من التشغيل. قد يؤدي ذلك إلى استغراق المزيد من الوقت عند بدء الجهاز اللوحي والسماح للتطبيق بإبطاء الأداء الإجمالي للجهاز اللوحي من خلال تشغيله دائمًا."</string>
<string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"للسماح بتشغيل التطبيق تلقائيًا بعد الانتهاء من بدء تشغيل النظام. وقد يؤدي ذلك إلى إطالة فترة بدء تشغيل جهاز Android TV، بالإضافة إلى أنه يسمح للتطبيق بإبطاء أداء الجهاز بشكل عام لأنه يتم تشغيله بشكل دائم."</string>
@@ -509,7 +509,7 @@
<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="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>
@@ -525,9 +525,9 @@
<string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"للسماح للتطبيق بتوصيل جهاز Android TV بشبكات WiMAX وقطع اتصاله بها."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"للسماح للتطبيق بتوصيل الهاتف بشبكات WiMAX وقطع اتصاله بها."</string>
<string name="permlab_bluetooth" msgid="586333280736937209">"الاتصال بأجهزة بلوتوث"</string>
- <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"للسماح للتطبيق بعرض تهيئة البلوتوث على الجهاز اللوحي وإجراء اتصالات وقبولها مع الأجهزة المقترنة."</string>
+ <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"للسماح للتطبيق بعرض إعداد البلوتوث على الجهاز اللوحي وإجراء اتصالات وقبولها مع الأجهزة المقترنة."</string>
<string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"للسماح للتطبيق بعرض بيانات ضبط البلوتوث على جهاز Android TV وإجراء اتصالات مع الأجهزة المقترنة وقبولها."</string>
- <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"للسماح للتطبيق بعرض تهيئة البلوتوث على الهاتف وإجراء اتصالات وقبولها مع الأجهزة المقترنة."</string>
+ <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"للسماح للتطبيق بعرض إعداد البلوتوث على الهاتف وإجراء اتصالات وقبولها مع الأجهزة المقترنة."</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"معلومات الخدمات المدفوعة باستخدام الاتصال قصير المدى NFC المفضّل"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"يسمح هذا الإذن للتطبيق بالحصول على معلومات الخدمات المدفوعة باستخدام الاتصال قصير المدى NFC المفضّل، مثلاً المساعدات المسجّلة ووجهة المسار."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"التحكم في اتصال الحقل القريب"</string>
@@ -664,8 +664,8 @@
<string name="permdesc_bindConditionProviderService" msgid="6106018791256120258">"للسماح للمالك بالربط بواجهة المستوى العلوي لخدمة موفر الحالة. لن تكون هناك حاجة إلى هذا الإعداد مطلقًا مع التطبيقات العادية."</string>
<string name="permlab_bindDreamService" msgid="4776175992848982706">"الالتزام بخدمة dream"</string>
<string name="permdesc_bindDreamService" msgid="9129615743300572973">"للسماح للمالك بالالتزام بواجهة المستوى العلوي لخدمة dream. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
- <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"استدعاء تطبيق التهيئة الذي يوفره مشغل شبكة الجوال"</string>
- <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"للسماح للمالك باستدعاء تطبيق التهيئة الذي يوفره مشغل شبكة الجوال. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
+ <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"استدعاء تطبيق الإعداد الذي يوفره مشغل شبكة الجوال"</string>
+ <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"للسماح للمالك باستدعاء تطبيق الإعداد الذي يوفره مشغل شبكة الجوال. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
<string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"الاستماع إلى ملاحظات حول أحوال الشبكة"</string>
<string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"للسماح للتطبيق بالاستماع إلى ملاحظات حول أحوال الشبكة. لا حاجة إلى هذا مع التطبيقات العادية."</string>
<string name="permlab_setInputCalibration" msgid="932069700285223434">"تغيير معايرة أجهزة الإدخال"</string>
@@ -681,7 +681,7 @@
<string name="permlab_bindCarrierServices" msgid="2395596978626237474">"الالتزام بخدمات مشغل شبكة الجوال"</string>
<string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"للسماح للمالك بالالتزام بخدمات مشغل شبكة الجوال. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
<string name="permlab_access_notification_policy" msgid="5524112842876975537">"الوصول إلى إعداد \"عدم الإزعاج\""</string>
- <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"للسماح للتطبيق بقراءة تهيئة \"عدم الإزعاج\" وكتابتها."</string>
+ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"للسماح للتطبيق بقراءة إعداد \"عدم الإزعاج\" وكتابتها."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"بدء استخدام إذن العرض"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"للسماح للمالك ببدء استخدام الإذن لأحد التطبيقات. ولن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"تعيين قواعد كلمة المرور"</string>
@@ -1465,7 +1465,7 @@
<string name="ext_media_status_unmountable" msgid="7043574843541087748">"تالف"</string>
<string name="ext_media_status_unsupported" msgid="5460509911660539317">"غير متوافق"</string>
<string name="ext_media_status_ejecting" msgid="7532403368044013797">"جارٍ إنهاء التحميل…"</string>
- <string name="ext_media_status_formatting" msgid="774148701503179906">"جارٍ التهيئة…"</string>
+ <string name="ext_media_status_formatting" msgid="774148701503179906">"تجري التهيئة..."</string>
<string name="ext_media_status_missing" msgid="6520746443048867314">"لم يتم الإدخال"</string>
<string name="activity_list_empty" msgid="4219430010716034252">"لم يتم العثور على أي أنشطة متطابقة."</string>
<string name="permlab_route_media_output" msgid="8048124531439513118">"توجيه إخراج الوسائط"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 95f7439..29666a9 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -714,7 +714,7 @@
<item msgid="6216981255272016212">"Aangepast"</item>
</string-array>
<string-array name="emailAddressTypes">
- <item msgid="7786349763648997741">"Thuis"</item>
+ <item msgid="7786349763648997741">"Privé"</item>
<item msgid="435564470865989199">"Werk"</item>
<item msgid="4199433197875490373">"Overig"</item>
<item msgid="3233938986670468328">"Aangepast"</item>
@@ -772,7 +772,7 @@
<string name="eventTypeAnniversary" msgid="4684702412407916888">"Jubileum"</string>
<string name="eventTypeOther" msgid="530671238533887997">"Overig"</string>
<string name="emailTypeCustom" msgid="1809435350482181786">"Aangepast"</string>
- <string name="emailTypeHome" msgid="1597116303154775999">"Thuis"</string>
+ <string name="emailTypeHome" msgid="1597116303154775999">"Privé"</string>
<string name="emailTypeWork" msgid="2020095414401882111">"Werk"</string>
<string name="emailTypeOther" msgid="5131130857030897465">"Overig"</string>
<string name="emailTypeMobile" msgid="787155077375364230">"Mobiel"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 050c1c4..ed15970 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -273,7 +273,7 @@
<flag name="runtime" value="0x2000" />
<!-- Additional flag from base permission type: this permission can be granted only
if its protection level is signature, the requesting app resides on the OEM partition,
- and the OEM has white-listed the app to receive this permission by the OEM.
+ and the OEM has allowlisted the app to receive this permission by the OEM.
-->
<flag name="oem" value="0x4000" />
<!-- Additional flag from base permission type: this permission can be granted to
@@ -340,7 +340,7 @@
<flag name="softRestricted" value="0x8" />
<!-- This permission is restricted immutably which means that its
restriction state may be specified only on the first install of
- the app and will stay in this initial whitelist state until
+ the app and will stay in this initial allowlist state until
the app is uninstalled.
-->
<flag name="immutablyRestricted" value="0x10" />
@@ -1409,13 +1409,13 @@
<attr name="lockTaskMode">
<!-- This is the default value. Tasks will not launch into lockTask mode but can be
placed there by calling {@link android.app.Activity#startLockTask}. If a task with
- this mode has been whitelisted using {@link
+ this mode has been allowlisted using {@link
android.app.admin.DevicePolicyManager#setLockTaskPackages} then calling
{@link android.app.Activity#startLockTask} will enter lockTask mode immediately,
otherwise the user will be presented with a dialog to approve entering pinned mode.
<p>If the system is already in lockTask mode when a new task rooted at this activity
is launched that task will or will not start depending on whether the package of this
- activity has been whitelisted.
+ activity has been allowlisted.
<p>Tasks rooted at this activity can only exit lockTask mode using
{@link android.app.Activity#stopLockTask}. -->
<enum name="normal" value="0"/>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f23e69d..d074eeb5f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -364,7 +364,7 @@
less than 0x600 -->
<bool translatable="false" name="config_apfDrop802_3Frames">true</bool>
- <!-- An array of Black listed EtherType, packets with EtherTypes within this array
+ <!-- An array of Denylisted EtherType, packets with EtherTypes within this array
will be dropped
TODO: need to put proper values, these are for testing purposes only -->
<integer-array translatable="false" name="config_apfEthTypeBlackList">
@@ -1166,6 +1166,7 @@
0 - Nothing
1 - Launch all apps intent
2 - Launch assist intent
+ 3 - Launch notification panel
This needs to match the constants in
policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
-->
@@ -1539,7 +1540,7 @@
<!-- Class name of WallpaperManagerService. -->
<string name="config_wallpaperManagerServiceName" translatable="false">com.android.server.wallpaper.WallpaperManagerService</string>
- <!-- Enables the TimeZoneRuleManager service. This is the master switch for the updateable time
+ <!-- Enables the TimeZoneRuleManager service. This is the global switch for the updateable time
zone update mechanism. -->
<bool name="config_enableUpdateableTimeZoneRules">false</bool>
@@ -2127,6 +2128,9 @@
<!-- Type of the long press sensor. Empty if long press is not supported. -->
<string name="config_dozeLongPressSensorType" translatable="false"></string>
+ <!-- Type of the udfps long press sensor. Empty if long press is not supported. -->
+ <string name="config_dozeUdfpsLongPressSensorType" translatable="false"></string>
+
<!-- If the sensor that wakes up the lock screen is available or not. -->
<bool name="config_dozeWakeLockScreenSensorAvailable">false</bool>
<integer name="config_dozeWakeLockScreenDebounce">300</integer>
@@ -2309,18 +2313,18 @@
will be locked. -->
<bool name="config_multiuserDelayUserDataLocking">false</bool>
- <!-- Whether to only install system packages on a user if they're whitelisted for that user
+ <!-- Whether to only install system packages on a user if they're allowlisted for that user
type. These are flags and can be freely combined.
- 0 - disable whitelist (install all system packages; no logging)
- 1 - enforce (only install system packages if they are whitelisted)
- 2 - log (log non-whitelisted packages)
- 4 - any package not mentioned in the whitelist file is implicitly whitelisted on all users
+ 0 - disable allowlist (install all system packages; no logging)
+ 1 - enforce (only install system packages if they are allowlisted)
+ 2 - log (log non-allowlisted packages)
+ 4 - any package not mentioned in the allowlist file is implicitly allowlisted on all users
8 - same as 4, but just for the SYSTEM user
16 - ignore OTAs (don't install system packages during OTAs)
Common scenarios:
- - to enable feature (fully enforced) for a complete whitelist: 1
- - to enable feature for an incomplete whitelist (so use implicit whitelist mode): 5
- - to enable feature but implicitly whitelist for SYSTEM user to ease local development: 9
+ - to enable feature (fully enforced) for a complete allowlist: 1
+ - to enable feature for an incomplete allowlist (so use implicit allowlist mode): 5
+ - to enable feature but implicitly allowlist for SYSTEM user to ease local development: 9
- to disable feature completely if it had never been enabled: 16
- to henceforth disable feature and try to undo its previous effects: 0
Note: This list must be kept current with PACKAGE_WHITELIST_MODE_PROP in
@@ -2498,25 +2502,25 @@
Example: com.google.android.myapp/.resolver.MyResolverActivity -->
<string name="config_customResolverActivity" translatable="false"></string>
- <!-- Name of the activity or service that prompts the user to reject, accept, or whitelist
+ <!-- Name of the activity or service that prompts the user to reject, accept, or allowlist
an adb host's public key, when an unwhitelisted host connects to the local adbd.
Can be customized for other product types -->
<string name="config_customAdbPublicKeyConfirmationComponent"
>com.android.systemui/com.android.systemui.usb.UsbDebuggingActivity</string>
- <!-- Name of the activity that prompts the secondary user to acknowledge she/he needs to
+ <!-- Name of the activity that prompts the secondary user to acknowledge they need to
switch to the primary user to enable USB debugging.
Can be customized for other product types -->
<string name="config_customAdbPublicKeyConfirmationSecondaryUserComponent"
>com.android.systemui/com.android.systemui.usb.UsbDebuggingSecondaryUserActivity</string>
- <!-- Name of the activity or service that prompts the user to reject, accept, or whitelist
+ <!-- Name of the activity or service that prompts the user to reject, accept, or allowlist
a wireless network for wireless debugging.
Can be customized for other product types -->
<string name="config_customAdbWifiNetworkConfirmationComponent"
>com.android.systemui/com.android.systemui.wifi.WifiDebuggingActivity</string>
- <!-- Name of the activity that prompts the secondary user to acknowledge she/he needs to
+ <!-- Name of the activity that prompts the secondary user to acknowledge they need to
switch to the primary user to enable wireless debugging.
Can be customized for other product types -->
<string name="config_customAdbWifiNetworkConfirmationSecondaryUserComponent"
@@ -2564,7 +2568,7 @@
<string name="config_appsAuthorizedForSharedAccounts" translatable="false">;com.android.settings;</string>
<!-- Flag indicating that the media framework should not allow changes or mute on any
- stream or master volumes. -->
+ stream or global volumes. -->
<bool name="config_useFixedVolume">false</bool>
<!-- The list of IMEs which should be disabled until used.
@@ -3263,13 +3267,12 @@
<!-- Array of values used in Gesture Navigation settings page to reduce/increase the back
gesture's inset size. These values will be multiplied into the default width, read from the
- gesture navigation overlay package, in order to create 4 different sizes which are selectable
+ gesture navigation overlay package, in order to create 3 different sizes which are selectable
via a slider component. -->
<array name="config_backGestureInsetScales">
- <item>0.75</item>
+ <item>0.60</item>
<item>1.00</item>
<item>1.33</item>
- <item>1.66</item>
</array>
<!-- Controls whether the navbar needs a scrim with
@@ -3390,6 +3393,9 @@
service. -->
<string name="config_tvRemoteServicePackage" translatable="false"></string>
+ <!-- The package name of the package implementing the custom notification panel -->
+ <string name="config_notificationHandlerPackage" translatable="false"></string>
+
<!-- True if the device supports persisting security logs across reboots.
This requires the device's kernel to have pstore and pmsg enabled,
and DRAM to be powered and refreshed through all stages of reboot. -->
@@ -3498,6 +3504,7 @@
mode -->
<string-array translatable="false" name="config_priorityOnlyDndExemptPackages">
<item>com.android.dialer</item>
+ <item>com.android.server.telecom</item>
<item>com.android.systemui</item>
<item>android</item>
</string-array>
@@ -4082,7 +4089,7 @@
<!-- All of the paths defined for the batterymeter are defined on a 12x20 canvas, and must
be parsable by android.utill.PathParser -->
<string name="config_batterymeterPerimeterPath" translatable="false">
- M3.5,2 v0 H1.33 C0.6,2 0,2.6 0,3.33 V13v5.67 C0,19.4 0.6,20 1.33,20 h9.33 C11.4,20 12,19.4 12,18.67 V13V3.33 C12,2.6 11.4,2 10.67,2 H8.5 V0 H3.5 z M2,18v-7V4h8v9v5H2L2,18z
+ M3.5,2 v0 H1.33 C0.6,2 0,2.6 0,3.33 V13v5.67 C0,19.4 0.6,20 1.33,20 h9.33 C11.4,20 12,19.4 12,18.67 V13V3.33 C12,2.6 11.4,2 10.67,2 H8.5 V0 H3.5 z M2,18v-7V4h8v9v5H2L2,18z
</string>
<string name="config_batterymeterErrorPerimeterPath" translatable="false">@string/config_batterymeterPerimeterPath</string>
<string name="config_batterymeterFillMask" translatable="false">
@@ -4278,10 +4285,10 @@
we only enabled it while the device has ability of mixed color spaces composition -->
<bool name="config_enableWcgMode">false</bool>
- <!-- When true, enables the whitelisted app to handle bug reports from power menu short press. -->
+ <!-- When true, enables the allowlisted app to handle bug reports from power menu short press. -->
<bool name="config_bugReportHandlerEnabled">false</bool>
- <!-- The package name for the default bug report handler app from power menu short press. This app must be whitelisted. -->
+ <!-- The package name for the default bug report handler app from power menu short press. This app must be allowlisted. -->
<string name="config_defaultBugReportHandlerApp" translatable="false"></string>
<!-- The default value used for RawContacts.ACCOUNT_NAME when contacts are inserted without this
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4d74cf7..03975da 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -354,11 +354,11 @@
<!-- A notification is shown when the AccountManager is unable to
supply an auth token without prompting the user to re-enter the
password. This is the text that will scroll through the
- notification bar (will be seen by the user as he uses another application). -->
+ notification bar (will be seen by the user as they use another application). -->
<string name="notification_title">Signin error for <xliff:g id="account" example="foo@gmail.com">%1$s</xliff:g></string>
<!-- Sync notifications --> <skip />
- <!-- A notification is shown when there is a sync error. This is the text that will scroll through the notification bar (will be seen by the user as he uses another application). -->
+ <!-- A notification is shown when there is a sync error. This is the text that will scroll through the notification bar (will be seen by the user as they use another application). -->
<string name="contentServiceSync">Sync</string>
<!-- A notification is shown when there is a sync error. This is the title of the notification. It will be seen in the pull-down notification tray. [CHAR LIMIT=NOTIF_TITLE] -->
<string name="contentServiceSyncNotificationTitle">Can\'t sync</string>
@@ -3126,7 +3126,7 @@
<string name="use_a_different_app">Use a different app</string>
<!-- Text displayed when the user selects the check box for setting default application. See the "Use by default for this action" check box. -->
<string name="clearDefaultHintMsg">Clear default in System settings > Apps > Downloaded.</string>
- <!-- Default title for the activity chooser, when one is not given. Android allows multiple activities to perform an action. for example, there may be many ringtone pickers installed. A dialog is shown to the user allowing him to pick which activity should be used. This is the title. -->
+ <!-- Default title for the activity chooser, when one is not given. Android allows multiple activities to perform an action. for example, there may be many ringtone pickers installed. A dialog is shown to the user allowing them to pick which activity should be used. This is the title. -->
<string name="chooseActivity">Choose an action</string>
<!-- title for the USB activity chooser. -->
<string name="chooseUsbActivity">Choose an app for the USB device</string>
@@ -3346,7 +3346,7 @@
<string name="ringtone_picker_title_alarm">Alarm sounds</string>
<!-- The title of the notification sound picker dialog [CHAR LIMIT=100] -->
<string name="ringtone_picker_title_notification">Notification sounds</string>
- <!-- If there is ever a ringtone set for some setting, but that ringtone can no longer be resolved, t his is shown instead. For example, if the ringtone was on a SD card and it had been removed, this woudl be shown for ringtones on that SD card. -->
+ <!-- If there is ever a ringtone set for some setting, but that ringtone can no longer be resolved, this is shown instead. For example, if the ringtone was on a SD card and it had been removed, this would be shown for ringtones on that SD card. -->
<string name="ringtone_unknown">Unknown</string>
<!-- A notification is shown when a wifi captive portal network is detected. This is the notification's title. -->
@@ -3905,7 +3905,7 @@
<string name="gpsNotifTitle">Location request</string>
<!-- Network positioning notification message. The name of the user (e.g. John Doe) and
service (SUPL-service) who sent the request is shown as dynamic strings.
- Translation should not be longer than master text. -->
+ Translation should not be longer than example text. -->
<string name="gpsNotifMessage">Requested by <xliff:g id="name">%1$s</xliff:g> (<xliff:g id="service" example="SUPL-service">%2$s</xliff:g>)</string>
<!-- Network positioning verification Yes. Button to push to share location information. -->
<string name="gpsVerifYes">Yes</string>
@@ -4888,6 +4888,9 @@
<item quantity="other">For %d hr</item>
</plurals>
+ <!-- Zen mode condition - line two: ending time indicating the next day. [CHAR LIMIT=NONE] -->
+ <string name="zen_mode_until_next_day">Until <xliff:g id="formattedTime" example="Tue, 10 PM">%1$s</xliff:g></string>
+
<!-- Zen mode condition - line two: ending time. [CHAR LIMIT=NONE] -->
<string name="zen_mode_until">Until <xliff:g id="formattedTime" example="10:00 PM">%1$s</xliff:g></string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index bd359ee..996e1f9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2393,6 +2393,7 @@
<java-symbol type="plurals" name="zen_mode_duration_hours_short" />
<java-symbol type="plurals" name="zen_mode_duration_minutes_summary_short" />
<java-symbol type="plurals" name="zen_mode_duration_hours_summary_short" />
+ <java-symbol type="string" name="zen_mode_until_next_day" />
<java-symbol type="string" name="zen_mode_until" />
<java-symbol type="string" name="zen_mode_feature_name" />
<java-symbol type="string" name="zen_mode_downtime_feature_name" />
@@ -3047,6 +3048,9 @@
<java-symbol type="string" name="config_tvRemoteServicePackage" />
<java-symbol type="string" name="notification_messaging_title_template" />
+ <!-- Notification handler / dashboard package -->
+ <java-symbol type="string" name="config_notificationHandlerPackage" />
+
<java-symbol type="bool" name="config_supportPreRebootSecurityLogs" />
<java-symbol type="dimen" name="notification_media_image_margin_end" />
@@ -3484,6 +3488,7 @@
<java-symbol type="array" name="config_hideWhenDisabled_packageNames" />
<java-symbol type="string" name="config_dozeLongPressSensorType" />
+ <java-symbol type="string" name="config_dozeUdfpsLongPressSensorType" />
<java-symbol type="bool" name="config_dozeWakeLockScreenSensorAvailable" />
<java-symbol type="integer" name="config_dozeWakeLockScreenDebounce" />
diff --git a/core/tests/PackageInstallerSessions/Android.bp b/core/tests/PackageInstallerSessions/Android.bp
index e74f30e..055249d 100644
--- a/core/tests/PackageInstallerSessions/Android.bp
+++ b/core/tests/PackageInstallerSessions/Android.bp
@@ -36,6 +36,11 @@
"framework-res",
],
+ java_resources: [
+ // Borrow an arbitrary test app from another module
+ ":PackageManagerTestAppVersion1",
+ ],
+
platform_apis: true,
sdk_version: "core_platform",
test_suites: ["device-tests"],
diff --git a/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt b/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt
index 494c92a..b410189 100644
--- a/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt
+++ b/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt
@@ -16,7 +16,12 @@
package android.content.pm
+import android.app.Instrumentation
+import android.app.PendingIntent
+import android.content.BroadcastReceiver
import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
import android.content.pm.PackageInstaller.SessionParams
import android.platform.test.annotations.Presubmit
import androidx.test.InstrumentationRegistry
@@ -27,6 +32,8 @@
import org.junit.Before
import org.junit.Test
import org.testng.Assert.assertThrows
+import java.util.concurrent.ArrayBlockingQueue
+import java.util.concurrent.TimeUnit
import kotlin.random.Random
/**
@@ -53,15 +60,49 @@
"android.permission.WRITE_CALL_LOG",
"android.permission.PROCESS_OUTGOING_CALLS"
)
+
+ private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
+ private const val INTENT_ACTION = "com.android.server.pm.test.test_app.action"
}
private val context: Context = InstrumentationRegistry.getContext()
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val installer = context.packageManager.packageInstaller
+ private val receiver = object : BroadcastReceiver() {
+ private val results = ArrayBlockingQueue<Intent>(1)
+
+ override fun onReceive(context: Context, intent: Intent) {
+ results.add(intent)
+ }
+
+ fun makeIntentSender(sessionId: Int) = PendingIntent.getBroadcast(context, sessionId,
+ Intent(INTENT_ACTION), PendingIntent.FLAG_UPDATE_CURRENT).intentSender
+
+ fun getResult(unit: TimeUnit, timeout: Long) = results.poll(timeout, unit)
+
+ fun clear() = results.clear()
+ }
+
+ @Before
+ fun registerReceiver() {
+ receiver.clear()
+ context.registerReceiver(receiver, IntentFilter(INTENT_ACTION))
+ }
+
+ @After
+ fun unregisterReceiver() {
+ context.unregisterReceiver(receiver)
+ }
+
@Before
@After
fun abandonAllSessions() {
+ instrumentation.uiAutomation
+ .executeShellCommand("pm uninstall com.android.server.pm.test.test_app")
+ .close()
+
installer.mySessions.asSequence()
.map { it.sessionId }
.forEach {
@@ -82,7 +123,7 @@
setAppLabel(longLabel)
}
- createSession(params) {
+ createAndAbandonSession(params) {
assertThat(installer.getSessionInfo(it)?.appLabel)
.isEqualTo(longLabel.take(PackageItemInfo.MAX_SAFE_LABEL_LENGTH))
}
@@ -95,7 +136,7 @@
setAppPackageName(longName)
}
- createSession(params) {
+ createAndAbandonSession(params) {
assertThat(installer.getSessionInfo(it)?.appPackageName)
.isEqualTo(null)
}
@@ -108,7 +149,7 @@
setInstallerPackageName(longName)
}
- createSession(params) {
+ createAndAbandonSession(params) {
// If a custom installer name is dropped, it defaults to the caller
assertThat(installer.getSessionInfo(it)?.installerPackageName)
.isEqualTo(context.packageName)
@@ -121,12 +162,39 @@
setWhitelistedRestrictedPermissions(invalidPermissions())
}
- createSession(params) {
+ createAndAbandonSession(params) {
assertThat(installer.getSessionInfo(it)?.whitelistedRestrictedPermissions!!)
.containsExactlyElementsIn(RESTRICTED_PERMISSIONS)
}
}
+ @Test
+ fun fillPackageNameWithParsedValue() {
+ val params = SessionParams(SessionParams.MODE_FULL_INSTALL)
+ val sessionId = installer.createSession(params)
+ val session = installer.openSession(sessionId)
+
+ javaClass.classLoader.getResourceAsStream("PackageManagerTestAppVersion1.apk")!!
+ .use { input ->
+ session.openWrite("base", 0, -1)
+ .use { output -> input.copyTo(output) }
+ }
+
+ // Test instrumentation doesn't have install permissions, so use shell
+ ShellIdentityUtils.invokeWithShellPermissions {
+ session.commit(receiver.makeIntentSender(sessionId))
+ }
+ session.close()
+
+ // The actual contents aren't verified as part of this test. Only care about it finishing.
+ val result = receiver.getResult(TimeUnit.SECONDS, 30)
+ assertThat(result).isNotNull()
+
+ val sessionInfo = installer.getSessionInfo(sessionId)
+ assertThat(sessionInfo).isNotNull()
+ assertThat(sessionInfo!!.getAppPackageName()).isEqualTo(TEST_PKG_NAME)
+ }
+
@LargeTest
@Test
fun allocateMaxSessionsWithPermission() {
@@ -177,7 +245,7 @@
repeat(10) { add(invalidPackageName(300)) }
}
- private fun createSession(params: SessionParams, block: (Int) -> Unit = {}) {
+ private fun createAndAbandonSession(params: SessionParams, block: (Int) -> Unit = {}) {
val sessionId = installer.createSession(params)
try {
block(sessionId)
diff --git a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
index 6328101..0c351d1 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
@@ -269,7 +269,7 @@
return unmarshalled;
}
- // If any entries in appear in the list, sanity check them against all running applications
+ // If any entries in appear in the list, validity check them against all running applications
private void checkErrorListSanity(List<ActivityManager.ProcessErrorStateInfo> errList) {
if (errList == null) return;
@@ -277,7 +277,7 @@
while (iter.hasNext()) {
ActivityManager.ProcessErrorStateInfo info = iter.next();
assertNotNull(info);
- // sanity checks
+ // validity checks
assertTrue((info.condition == ActivityManager.ProcessErrorStateInfo.CRASHED) ||
(info.condition == ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING));
// TODO look at each of these and consider a stronger test
diff --git a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
index 1f4e5df..da386a6 100644
--- a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
+++ b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
@@ -277,7 +277,7 @@
try {
// Write to parcel
- parcel.setDataPosition(0); // Sanity / paranoid check
+ parcel.setDataPosition(0); // Validity Check
structure.writeToParcel(parcel, NO_FLAGS);
// Read from parcel
diff --git a/core/tests/coretests/src/android/content/FakeProviderLocal.java b/core/tests/coretests/src/android/content/FakeProviderLocal.java
index a8c2f40..29f054b 100644
--- a/core/tests/coretests/src/android/content/FakeProviderLocal.java
+++ b/core/tests/coretests/src/android/content/FakeProviderLocal.java
@@ -20,7 +20,7 @@
import android.net.Uri;
/**
- * A dummy content provider for tests. This provider runs in the same process as the test.
+ * A placeholder content provider for tests. This provider runs in the same process as the test.
*/
public class FakeProviderLocal extends ContentProvider {
diff --git a/core/tests/coretests/src/android/content/FakeProviderRemote.java b/core/tests/coretests/src/android/content/FakeProviderRemote.java
index 8bc5660..2c92da3 100644
--- a/core/tests/coretests/src/android/content/FakeProviderRemote.java
+++ b/core/tests/coretests/src/android/content/FakeProviderRemote.java
@@ -20,7 +20,7 @@
import android.net.Uri;
/**
- * A dummy content provider for tests. This provider runs in a different process from the test.
+ * A placeholder content provider for tests. This provider runs in a different process from the test.
*/
public class FakeProviderRemote extends ContentProvider {
diff --git a/core/tests/coretests/src/android/content/SlowProvider.java b/core/tests/coretests/src/android/content/SlowProvider.java
index aba32e8..2ba0eff 100644
--- a/core/tests/coretests/src/android/content/SlowProvider.java
+++ b/core/tests/coretests/src/android/content/SlowProvider.java
@@ -20,8 +20,8 @@
import android.net.Uri;
/**
- * A dummy content provider for tests. This provider runs in a different process from the test and
- * is intentionally slow.
+ * A placeholder content provider for tests. This provider runs in a
+ * different process from the test and is intentionally slow.
*/
public class SlowProvider extends ContentProvider {
diff --git a/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java
index ea69176..7af96c3 100644
--- a/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java
@@ -486,7 +486,7 @@
assertThat(boolFormula.isInstallerFormula()).isFalse();
}
- /** Returns a builder with all fields filled with some dummy data. */
+ /** Returns a builder with all fields filled with some placeholder data. */
private AppInstallMetadata.Builder getAppInstallMetadataBuilder() {
return new AppInstallMetadata.Builder()
.setPackageName("abc")
diff --git a/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java b/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
index abc5fed..ba060fa 100644
--- a/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
@@ -282,7 +282,7 @@
assertThat(compoundFormula.isInstallerFormula()).isTrue();
}
- /** Returns a builder with all fields filled with some dummy data. */
+ /** Returns a builder with all fields filled with some placeholder data. */
private AppInstallMetadata.Builder getAppInstallMetadataBuilder() {
return new AppInstallMetadata.Builder()
.setPackageName("abc")
diff --git a/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java b/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java
index 693d4ca..70712e4 100644
--- a/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java
@@ -110,7 +110,7 @@
assertThat(FORMULA.matches(appInstallMetadata)).isTrue();
}
- /** Returns a builder with all fields filled with some dummy data. */
+ /** Returns a builder with all fields filled with some placeholder data. */
private AppInstallMetadata.Builder getAppInstallMetadataBuilder() {
return new AppInstallMetadata.Builder()
.setPackageName("abc")
diff --git a/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java b/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java
index f84355f..ced1f4d 100644
--- a/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/SearchRecentSuggestionsProviderTest.java
@@ -316,7 +316,7 @@
private void checkResultCounts(String queryString, int minRows, int maxRows,
String matchDisplay1, String matchDisplay2) {
- // get the cursor and apply sanity checks to result
+ // get the cursor and apply validity checks to result
Cursor c = getQueryCursor(queryString);
assertNotNull(c);
assertTrue("Insufficient rows in filtered cursor", c.getCount() >= minRows);
diff --git a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
index b329e55..88f2313 100644
--- a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
+++ b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java
@@ -318,7 +318,7 @@
try {
// Write to parcel
- parcel.setDataPosition(0); // Sanity / paranoid check
+ parcel.setDataPosition(0); // Validity Check
id.writeToParcel(parcel, 0);
// Read from parcel
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
index 2008537..67614bb 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
@@ -317,7 +317,7 @@
try {
// Write to parcel
- parcel.setDataPosition(0); // Sanity / paranoid check
+ parcel.setDataPosition(0); // Validity Check
event.writeToParcel(parcel, 0);
// Read from parcel
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index 5ea0718..eb58c63 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -66,7 +66,7 @@
@Test
public void testNewAutofillId_differentSessions() {
- assertThat(mSession1.getId()).isNotEqualTo(mSession2.getId()); //sanity check
+ assertThat(mSession1.getId()).isNotEqualTo(mSession2.getId()); //validity check
final AutofillId parentId = new AutofillId(42);
final AutofillId childId1 = mSession1.newAutofillId(parentId, 108L);
final AutofillId childId2 = mSession2.newAutofillId(parentId, 108L);
@@ -84,7 +84,7 @@
@Test
public void testNewViewStructure() {
- assertThat(mMockView.getAutofillId()).isNotNull(); // sanity check
+ assertThat(mMockView.getAutofillId()).isNotNull(); // validity check
final ViewStructure structure = mSession1.newViewStructure(mMockView);
assertThat(structure).isNotNull();
assertThat(structure.getAutofillId()).isEqualTo(mMockView.getAutofillId());
diff --git a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
index 93de03a..5c41087 100644
--- a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
@@ -274,13 +274,10 @@
editorInfo.inputType = EditorInfo.TYPE_CLASS_TEXT;
editorInfo.setInitialSurroundingText(sb);
- sb.setLength(0);
- editorInfo.writeToParcel(parcel, 0);
+ sb.setLength(/* newLength= */ 0);
+ editorInfo.writeToParcel(parcel, /* flags= */ 0);
- try {
- editorInfo.getInitialTextBeforeCursor(60, 1);
- fail("Test shouldn't have exception");
- } catch (AssertionError e) { }
+ editorInfo.getInitialTextBeforeCursor(/* length= */ 60, /* flags= */ 1);
}
private static void assertExpectedTextLength(EditorInfo editorInfo,
diff --git a/core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml b/core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml
index b1c2a63..9cecaed 100644
--- a/core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml
+++ b/core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml
@@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.BATTERY_STATS"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.DEVICE_POWER"/>
<uses-permission android:name="android.permission.INTERNET"/>
<instrumentation
diff --git a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java
index 0cdb404..a71559b 100644
--- a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java
+++ b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java
@@ -16,6 +16,7 @@
package com.android.frameworks.core.powerstatsloadtests;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.app.Instrumentation;
@@ -34,6 +35,7 @@
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.compatibility.common.util.SystemUtil;
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.LoggingPrintStream;
@@ -45,6 +47,8 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
public class PowerMetricsCollector implements TestRule {
private final String mTag;
@@ -55,6 +59,7 @@
private final UserManager mUserManager;
private final int mUid;
private final BatteryStatsHelper mStatsHelper;
+ private final CountDownLatch mSuspendingBatteryInput = new CountDownLatch(1);
private long mStartTime;
private volatile float mInitialBatteryLevel;
@@ -63,12 +68,21 @@
private PowerMetrics mInitialPowerMetrics;
private PowerMetrics mFinalPowerMetrics;
private List<PowerMetrics.Metric> mPowerMetricsDelta;
+ private Intent mBatteryStatus;
@Override
public Statement apply(Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
+ BroadcastReceiver batteryBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ handleBatteryStatus(intent);
+ }
+ };
+ mBatteryStatus = mContext.registerReceiver(batteryBroadcastReceiver,
+ new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
disableCharger();
try {
prepareBatteryLevelMonitor();
@@ -76,6 +90,7 @@
base.evaluate();
captureFinalPowerStatsData();
} finally {
+ mContext.unregisterReceiver(batteryBroadcastReceiver);
enableCharger();
}
}
@@ -95,12 +110,14 @@
mStatsHelper.create((Bundle) null);
}
- private void disableCharger() {
- // TODO(b/167636754): implement this method once the charger suspension API is available
+ private void disableCharger() throws InterruptedException {
+ SystemUtil.runShellCommand("dumpsys battery suspend_input");
+ final boolean success = mSuspendingBatteryInput.await(10, TimeUnit.SECONDS);
+ assertTrue("Timed out waiting for battery input to be suspended", success);
}
private void enableCharger() {
- // TODO(b/167636754): implement this method once the charger suspension API is available
+ SystemUtil.runShellCommand("dumpsys battery reset");
}
private PowerMetrics readBatteryStatsData() {
@@ -111,19 +128,25 @@
}
protected void prepareBatteryLevelMonitor() {
- Intent batteryStatus = mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- handleBatteryStatus(intent);
- }
- }, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
-
- handleBatteryStatus(batteryStatus);
+ handleBatteryStatus(mBatteryStatus);
mInitialBatteryLevel = mCurrentBatteryLevel;
}
protected void handleBatteryStatus(Intent intent) {
- if (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) != 0) {
+ if (mFinalPowerMetrics != null) {
+ return;
+ }
+
+ final boolean isCharging = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) != 0;
+
+ if (mSuspendingBatteryInput.getCount() > 0) {
+ if (!isCharging) {
+ mSuspendingBatteryInput.countDown();
+ }
+ return;
+ }
+
+ if (isCharging) {
fail("Device must remain disconnected from the power source "
+ "for the duration of the test");
}
diff --git a/data/etc/car/com.android.car.carlauncher.xml b/data/etc/car/com.android.car.carlauncher.xml
index 8ec1cd4..0e49284 100644
--- a/data/etc/car/com.android.car.carlauncher.xml
+++ b/data/etc/car/com.android.car.carlauncher.xml
@@ -17,6 +17,7 @@
<permissions>
<privapp-permissions package="com.android.car.carlauncher">
<permission name="android.permission.ACTIVITY_EMBEDDING"/>
+ <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
<permission name="android.permission.PACKAGE_USAGE_STATS"/>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 602ccfe..102c933 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -226,6 +226,7 @@
<permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
<permission name="android.permission.UPDATE_DEVICE_STATS"/>
+ <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
</privapp-permissions>
<privapp-permissions package="com.android.providers.media.module">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index e693198..217de84 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -31,6 +31,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "-2107721178": {
+ "message": "grantEmbeddedWindowFocus win=%s grantFocus=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_FOCUS",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"-2101985723": {
"message": "Failed looking up window session=%s callers=%s",
"level": "WARN",
@@ -1597,6 +1603,12 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "397105698": {
+ "message": "grantEmbeddedWindowFocus remove request for win=%s dropped since no candidate was found",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_FOCUS",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"399841913": {
"message": "SURFACE RECOVER DESTROY: %s",
"level": "INFO",
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index bd2d4af..6ac73b1 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -223,7 +223,7 @@
key 201 MEDIA_PAUSE
# key 202 "KEY_PROG3"
# key 203 "KEY_PROG4"
-# key 204 (undefined)
+key 204 NOTIFICATION
# key 205 "KEY_SUSPEND"
# key 206 "KEY_CLOSE"
key 207 MEDIA_PLAY
diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java
index 42a5cc4..163823f 100644
--- a/graphics/java/android/graphics/FrameInfo.java
+++ b/graphics/java/android/graphics/FrameInfo.java
@@ -40,7 +40,7 @@
*/
public final class FrameInfo {
- public long[] frameInfo = new long[9];
+ public long[] frameInfo = new long[10];
// Various flags set to provide extra metadata about the current frame
private static final int FLAGS = 0;
@@ -51,38 +51,44 @@
// A renderer associated with just a Surface, not with a ViewRootImpl instance.
public static final long FLAG_SURFACE_CANVAS = 1 << 2;
+ // An invalid vsync id to be used when FRAME_TIMELINE_VSYNC_ID is unknown
+ public static final long INVALID_VSYNC_ID = -1;
+
@LongDef(flag = true, value = {
FLAG_WINDOW_LAYOUT_CHANGED, FLAG_SURFACE_CANVAS })
@Retention(RetentionPolicy.SOURCE)
public @interface FrameInfoFlags {}
+ private static final int FRAME_TIMELINE_VSYNC_ID = 1;
+
// The intended vsync time, unadjusted by jitter
- private static final int INTENDED_VSYNC = 1;
+ private static final int INTENDED_VSYNC = 2;
// Jitter-adjusted vsync time, this is what was used as input into the
// animation & drawing system
- private static final int VSYNC = 2;
+ private static final int VSYNC = 3;
// The time of the oldest input event
- private static final int OLDEST_INPUT_EVENT = 3;
+ private static final int OLDEST_INPUT_EVENT = 4;
// The time of the newest input event
- private static final int NEWEST_INPUT_EVENT = 4;
+ private static final int NEWEST_INPUT_EVENT = 5;
// When input event handling started
- private static final int HANDLE_INPUT_START = 5;
+ private static final int HANDLE_INPUT_START = 6;
// When animation evaluations started
- private static final int ANIMATION_START = 6;
+ private static final int ANIMATION_START = 7;
// When ViewRootImpl#performTraversals() started
- private static final int PERFORM_TRAVERSALS_START = 7;
+ private static final int PERFORM_TRAVERSALS_START = 8;
// When View:draw() started
- private static final int DRAW_START = 8;
+ private static final int DRAW_START = 9;
/** checkstyle */
- public void setVsync(long intendedVsync, long usedVsync) {
+ public void setVsync(long intendedVsync, long usedVsync, long frameTimelineVsyncId) {
+ frameInfo[FRAME_TIMELINE_VSYNC_ID] = frameTimelineVsyncId;
frameInfo[INTENDED_VSYNC] = intendedVsync;
frameInfo[VSYNC] = usedVsync;
frameInfo[OLDEST_INPUT_EVENT] = Long.MAX_VALUE;
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 0452933..fd5916c 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -354,7 +354,8 @@
* @return this instance
*/
public @NonNull FrameRenderRequest setVsyncTime(long vsyncTime) {
- mFrameInfo.setVsync(vsyncTime, vsyncTime);
+ // TODO(b/168552873): populate vsync Id once available to Choreographer public API
+ mFrameInfo.setVsync(vsyncTime, vsyncTime, FrameInfo.INVALID_VSYNC_ID);
mFrameInfo.addFlags(FrameInfo.FLAG_SURFACE_CANVAS);
return this;
}
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index 15d855e..a7d3f798 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -47,6 +47,7 @@
DEPTH16,
DEPTH_POINT_CLOUD,
RAW_DEPTH,
+ RAW_DEPTH10,
PRIVATE,
HEIC
})
@@ -725,6 +726,15 @@
public static final int RAW_DEPTH = 0x1002;
/**
+ * Unprocessed implementation-dependent raw
+ * depth measurements, opaque with 10 bit
+ * samples and device specific bit layout.
+ *
+ * @hide
+ */
+ public static final int RAW_DEPTH10 = 0x1003;
+
+ /**
* Android private opaque image format.
* <p>
* The choices of the actual format and pixel data layout are entirely up to
@@ -797,6 +807,7 @@
case RAW_DEPTH:
case RAW_SENSOR:
return 16;
+ case RAW_DEPTH10:
case RAW10:
return 10;
case RAW12:
@@ -838,6 +849,7 @@
case DEPTH_POINT_CLOUD:
case PRIVATE:
case RAW_DEPTH:
+ case RAW_DEPTH10:
case Y8:
case DEPTH_JPEG:
case HEIC:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index c84b478..d060f64 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -172,8 +172,10 @@
context, displayController);
OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
context, displayController, animationController, tutorialHandler);
+ IOverlayManager overlayManager = IOverlayManager.Stub.asInterface(
+ ServiceManager.getService(Context.OVERLAY_SERVICE));
return new OneHandedController(context, displayController, organizer, touchHandler,
- tutorialHandler, gestureHandler);
+ tutorialHandler, gestureHandler, overlayManager);
}
@VisibleForTesting
@@ -182,7 +184,8 @@
OneHandedDisplayAreaOrganizer displayAreaOrganizer,
OneHandedTouchHandler touchHandler,
OneHandedTutorialHandler tutorialHandler,
- OneHandedGestureHandler gestureHandler) {
+ OneHandedGestureHandler gestureHandler,
+ IOverlayManager overlayManager) {
mHasOneHandedFeature = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
if (!mHasOneHandedFeature) {
Log.i(TAG, "Device config SUPPORT_ONE_HANDED_MODE off");
@@ -194,32 +197,32 @@
mGestureHandler = null;
mTimeoutHandler = null;
mOverlayManager = null;
- return;
+ } else {
+ mContext = context;
+ mDisplayAreaOrganizer = displayAreaOrganizer;
+ mDisplayController = displayController;
+ mTouchHandler = touchHandler;
+ mTutorialHandler = tutorialHandler;
+ mGestureHandler = gestureHandler;
+ mOverlayManager = overlayManager;
+
+ mOffSetFraction = SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50)
+ / 100.0f;
+ mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
+ context.getContentResolver());
+ mIsSwipeToNotificationEnabled =
+ OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
+ context.getContentResolver());
+ mTimeoutHandler = OneHandedTimeoutHandler.get();
+
+ mDisplayController.addDisplayChangingController(mRotationController);
+
+ setupCallback();
+ setupSettingObservers();
+ setupTimeoutListener();
+ setupGesturalOverlay();
+ updateSettings();
}
-
- mContext = context;
- mDisplayAreaOrganizer = displayAreaOrganizer;
- mDisplayController = displayController;
- mTouchHandler = touchHandler;
- mTutorialHandler = tutorialHandler;
- mGestureHandler = gestureHandler;
-
- mOverlayManager = IOverlayManager.Stub.asInterface(
- ServiceManager.getService(Context.OVERLAY_SERVICE));
- mOffSetFraction = SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50) / 100.0f;
- mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
- context.getContentResolver());
- mIsSwipeToNotificationEnabled = OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
- context.getContentResolver());
- mTimeoutHandler = OneHandedTimeoutHandler.get();
-
- mDisplayController.addDisplayChangingController(mRotationController);
-
- setupCallback();
- setupSettingObservers();
- setupTimeoutListener();
- setupGesturalOverlay();
- updateSettings();
}
/**
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 ae09754..e3029e5 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
@@ -24,6 +24,8 @@
* This file is used by the ProtoLogTool to generate optimized logging code.
*/
public enum ShellProtoLogGroup implements IProtoLogGroup {
+ // NOTE: Since we enable these from the same WM ShellCommand, these names should not conflict
+ // with those in the framework ProtoLogGroup
WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java
index 6a925e7..66ecf45 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java
@@ -44,8 +44,6 @@
private static ShellProtoLogImpl sServiceInstance = null;
- private final PrintWriter mSystemOutWriter;
-
static {
addLogGroupEnum(ShellProtoLogGroup.values());
}
@@ -111,11 +109,11 @@
return sServiceInstance;
}
- public void startTextLogging(Context context, String... groups) {
+ public int startTextLogging(Context context, String[] groups, PrintWriter pw) {
try {
mViewerConfig.loadViewerConfig(
context.getResources().openRawResource(R.raw.wm_shell_protolog));
- setLogging(true /* setTextLogging */, true, mSystemOutWriter, groups);
+ return setLogging(true /* setTextLogging */, true, pw, groups);
} catch (IOException e) {
Log.i(TAG, "Unable to load log definitions: IOException while reading "
+ "wm_shell_protolog. " + e);
@@ -123,16 +121,15 @@
Log.i(TAG, "Unable to load log definitions: JSON parsing exception while reading "
+ "wm_shell_protolog. " + e);
}
+ return -1;
}
- public void stopTextLogging(String... groups) {
- setLogging(true /* setTextLogging */, false, mSystemOutWriter, groups);
+ public int stopTextLogging(String[] groups, PrintWriter pw) {
+ return setLogging(true /* setTextLogging */, false, pw, groups);
}
private ShellProtoLogImpl() {
- super(new File(LOG_FILENAME), null, BUFFER_CAPACITY,
- new ProtoLogViewerConfigReader());
- mSystemOutWriter = new PrintWriter(System.out, true);
+ super(new File(LOG_FILENAME), null, BUFFER_CAPACITY, new ProtoLogViewerConfigReader());
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 1ce8b54..3645f1e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -18,13 +18,13 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.om.IOverlayManager;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -62,6 +62,8 @@
OneHandedGestureHandler mMockGestureHandler;
@Mock
OneHandedTimeoutHandler mMockTimeoutHandler;
+ @Mock
+ IOverlayManager mMockOverlayManager;
@Before
public void setUp() throws Exception {
@@ -73,7 +75,8 @@
mMockDisplayAreaOrganizer,
mMockTouchHandler,
mMockTutorialHandler,
- mMockGestureHandler);
+ mMockGestureHandler,
+ mMockOverlayManager);
mOneHandedController = Mockito.spy(oneHandedController);
mTimeoutHandler = Mockito.spy(OneHandedTimeoutHandler.get());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
index 4a133d39..3341c9c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
@@ -18,6 +18,7 @@
import static org.mockito.Mockito.verify;
+import android.content.om.IOverlayManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -44,6 +45,8 @@
DisplayController mMockDisplayController;
@Mock
OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
+ @Mock
+ IOverlayManager mMockOverlayManager;
@Before
public void setUp() {
@@ -56,11 +59,12 @@
mMockDisplayAreaOrganizer,
mTouchHandler,
mTutorialHandler,
- mGestureHandler);
+ mGestureHandler,
+ mMockOverlayManager);
}
@Test
- public void testOneHandedManager_registerForDisplayAreaOrganizer() {
+ public void testRegisterForDisplayAreaOrganizer() {
verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mTutorialHandler);
}
}
diff --git a/libs/androidfw/include/androidfw/ConfigDescription.h b/libs/androidfw/include/androidfw/ConfigDescription.h
index acf413a..61d10cd 100644
--- a/libs/androidfw/include/androidfw/ConfigDescription.h
+++ b/libs/androidfw/include/androidfw/ConfigDescription.h
@@ -177,9 +177,8 @@
return *this;
}
-inline bool ConfigDescription::MatchWithDensity(
- const ConfigDescription& o) const {
- return match(o) && (density == 0 || density == o.density);
+inline bool ConfigDescription::MatchWithDensity(const ConfigDescription& o) const {
+ return match(o) && (density == 0 || o.density != 0);
}
inline bool ConfigDescription::operator<(const ConfigDescription& o) const {
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index 0698775..30ce537 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -22,6 +22,7 @@
const std::string FrameInfoNames[] = {
"Flags",
+ "FrameTimelineVsyncId",
"IntendedVsync",
"Vsync",
"OldestInputEvent",
@@ -44,7 +45,7 @@
static_cast<int>(FrameInfoIndex::NumIndexes),
"size mismatch: FrameInfoNames doesn't match the enum!");
-static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 17,
+static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 18,
"Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)");
void FrameInfo::importUiThreadInfo(int64_t* info) {
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index dc30617..f5bfedd 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -27,10 +27,11 @@
namespace android {
namespace uirenderer {
-#define UI_THREAD_FRAME_INFO_SIZE 9
+#define UI_THREAD_FRAME_INFO_SIZE 10
enum class FrameInfoIndex {
Flags = 0,
+ FrameTimelineVsyncId,
IntendedVsync,
Vsync,
OldestInputEvent,
@@ -71,11 +72,15 @@
class UiFrameInfoBuilder {
public:
+ static constexpr int64_t INVALID_VSYNC_ID = -1;
+
explicit UiFrameInfoBuilder(int64_t* buffer) : mBuffer(buffer) {
memset(mBuffer, 0, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
+ set(FrameInfoIndex::FrameTimelineVsyncId) = INVALID_VSYNC_ID;
}
- UiFrameInfoBuilder& setVsync(nsecs_t vsyncTime, nsecs_t intendedVsync) {
+ UiFrameInfoBuilder& setVsync(nsecs_t vsyncTime, nsecs_t intendedVsync, int64_t vsyncId) {
+ set(FrameInfoIndex::FrameTimelineVsyncId) = vsyncId;
set(FrameInfoIndex::Vsync) = vsyncTime;
set(FrameInfoIndex::IntendedVsync) = intendedVsync;
// Pretend the other fields are all at vsync, too, so that naive
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index e817ca7..c89463b 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -514,7 +514,7 @@
proxy.setLightGeometry((Vector3){0, 0, 0}, 0);
nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC);
UiFrameInfoBuilder(proxy.frameInfo())
- .setVsync(vsync, vsync)
+ .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID)
.addFlag(FrameInfoFlags::SurfaceCanvas);
proxy.syncAndDrawFrame();
}
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index dd0fc69..bfbdc5c 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -56,7 +56,8 @@
}
void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
- if (canvas->getGrContext() == nullptr) {
+ GrDirectContext* directContext = GrAsDirectContext(canvas->recordingContext());
+ if (directContext == nullptr) {
// We're dumping a picture, render a light-blue rectangle instead
// TODO: Draw the WebView text on top? Seemingly complicated as SkPaint doesn't
// seem to have a default typeface that works. We only ever use drawGlyphs, which
@@ -87,7 +88,7 @@
SkImageInfo surfaceInfo =
canvas->imageInfo().makeWH(clipBounds.width(), clipBounds.height());
tmpSurface =
- SkSurface::MakeRenderTarget(canvas->getGrContext(), SkBudgeted::kYes, surfaceInfo);
+ SkSurface::MakeRenderTarget(directContext, SkBudgeted::kYes, surfaceInfo);
tmpSurface->getCanvas()->clear(SK_ColorTRANSPARENT);
GrGLFramebufferInfo fboInfo;
@@ -141,7 +142,7 @@
// notify Skia that we just updated the FBO and stencil
const uint32_t grState = kStencil_GrGLBackendState | kRenderTarget_GrGLBackendState;
- canvas->getGrContext()->resetContext(grState);
+ directContext->resetContext(grState);
SkCanvas* tmpCanvas = canvas;
if (tmpSurface) {
@@ -188,7 +189,7 @@
glClear(GL_STENCIL_BUFFER_BIT);
}
- canvas->getGrContext()->resetContext();
+ directContext->resetContext();
// if there were unclipped save layers involved we draw our offscreen surface to the canvas
if (tmpSurface) {
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 6dd3698..6e7493c 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -86,7 +86,7 @@
}
void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) {
- sk_sp<GrContext> cachedContext;
+ sk_sp<GrDirectContext> cachedContext;
// Render all layers that need to be updated, in order.
for (size_t i = 0; i < layers.entries().size(); i++) {
@@ -142,7 +142,8 @@
// cache the current context so that we can defer flushing it until
// either all the layers have been rendered or the context changes
- GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext();
+ GrDirectContext* currentContext =
+ GrAsDirectContext(layerNode->getLayerSurface()->getCanvas()->recordingContext());
if (cachedContext.get() != currentContext) {
if (cachedContext.get()) {
ATRACE_NAME("flush layers (context changed)");
@@ -201,7 +202,7 @@
}
void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
- GrContext* context = thread.getGrContext();
+ GrDirectContext* context = thread.getGrContext();
if (context) {
ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height());
auto image = bitmap->makeImage();
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index 50b45e6..6efe176 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -96,7 +96,7 @@
// "VkFunctorDrawable::onDraw" is not invoked for the most common case, when drawing in a GPU
// canvas.
- if (canvas->getGrContext() == nullptr) {
+ if (canvas->recordingContext() == nullptr) {
// We're dumping a picture, render a light-blue rectangle instead
SkPaint paint;
paint.setColor(0xFF81D4FA);
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index 403d907..bc8ce42 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -67,7 +67,7 @@
void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) {
ATRACE_CALL();
- if (canvas->getGrContext() == nullptr) {
+ if (canvas->recordingContext() == nullptr) {
SkDEBUGF(("Attempting to draw VkInteropFunctor into an unsupported surface"));
return;
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 13d544c..c7560b2 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -484,6 +484,14 @@
waitOnFences();
+ if (mNativeSurface) {
+ // TODO(b/165985262): measure performance impact
+ if (const auto vsyncId = mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
+ vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) {
+ native_window_set_frame_timeline_vsync(mNativeSurface->getNativeWindow(), vsyncId);
+ }
+ }
+
bool requireSwap = false;
int error = OK;
bool didSwap =
@@ -617,8 +625,11 @@
ATRACE_CALL();
nsecs_t vsync = mRenderThread.timeLord().computeFrameTimeNanos();
+ int64_t vsyncId = mRenderThread.timeLord().lastVsyncId();
int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
- UiFrameInfoBuilder(frameInfo).addFlag(FrameInfoFlags::RTAnimation).setVsync(vsync, vsync);
+ UiFrameInfoBuilder(frameInfo)
+ .addFlag(FrameInfoFlags::RTAnimation)
+ .setVsync(vsync, vsync, vsyncId);
TreeInfo info(TreeInfo::MODE_RT_ONLY, *this);
prepareTree(info, frameInfo, systemTime(SYSTEM_TIME_MONOTONIC), node);
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index cba710f..cc4eb32 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -106,7 +106,7 @@
* If Properties::isSkiaEnabled() is true then this will return the Skia
* grContext associated with the current RenderPipeline.
*/
- GrContext* getGrContext() const { return mRenderThread.getGrContext(); }
+ GrDirectContext* getGrContext() const { return mRenderThread.getGrContext(); }
// Won't take effect until next EGLSurface creation
void setSwapBehavior(SwapBehavior swapBehavior);
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 1e59338..1ea595d 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -128,7 +128,9 @@
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
ATRACE_CALL();
int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
- mRenderThread->timeLord().vsyncReceived(vsync);
+ int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)];
+ int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
+ mRenderThread->timeLord().vsyncReceived(vsync, intendedVsync, vsyncId);
bool canDraw = mContext->makeCurrent();
mContext->unpinImages();
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 4dcbc44..9371656 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -51,8 +51,10 @@
void RenderThread::frameCallback(int64_t frameTimeNanos, void* data) {
RenderThread* rt = reinterpret_cast<RenderThread*>(data);
+ int64_t vsyncId = AChoreographer_getVsyncId(rt->mChoreographer);
rt->mVsyncRequested = false;
- if (rt->timeLord().vsyncReceived(frameTimeNanos) && !rt->mFrameCallbackTaskPending) {
+ if (rt->timeLord().vsyncReceived(frameTimeNanos, frameTimeNanos, vsyncId) &&
+ !rt->mFrameCallbackTaskPending) {
ATRACE_NAME("queue mFrameCallbackTask");
rt->mFrameCallbackTaskPending = true;
nsecs_t runAt = (frameTimeNanos + rt->mDispatchFrameDelay);
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index d7dc00b..4fbb0716 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -19,8 +19,8 @@
#include <GrDirectContext.h>
#include <SkBitmap.h>
-#include <apex/choreographer.h>
#include <cutils/compiler.h>
+#include <private/android/choreographer.h>
#include <thread/ThreadBase.h>
#include <utils/Looper.h>
#include <utils/Thread.h>
diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp
index 784068f..7dc36c4 100644
--- a/libs/hwui/renderthread/TimeLord.cpp
+++ b/libs/hwui/renderthread/TimeLord.cpp
@@ -19,9 +19,17 @@
namespace uirenderer {
namespace renderthread {
-TimeLord::TimeLord() : mFrameIntervalNanos(milliseconds_to_nanoseconds(16)), mFrameTimeNanos(0) {}
+TimeLord::TimeLord() : mFrameIntervalNanos(milliseconds_to_nanoseconds(16)),
+ mFrameTimeNanos(0),
+ mFrameIntendedTimeNanos(0),
+ mFrameVsyncId(-1) {}
-bool TimeLord::vsyncReceived(nsecs_t vsync) {
+bool TimeLord::vsyncReceived(nsecs_t vsync, nsecs_t intendedVsync, int64_t vsyncId) {
+ if (intendedVsync > mFrameIntendedTimeNanos) {
+ mFrameIntendedTimeNanos = intendedVsync;
+ mFrameVsyncId = vsyncId;
+ }
+
if (vsync > mFrameTimeNanos) {
mFrameTimeNanos = vsync;
return true;
@@ -36,6 +44,8 @@
if (jitterNanos >= mFrameIntervalNanos) {
nsecs_t lastFrameOffset = jitterNanos % mFrameIntervalNanos;
mFrameTimeNanos = now - lastFrameOffset;
+ // mFrameVsyncId is not adjusted here as we still want to send
+ // the vsync id that started this frame to the Surface Composer
}
return mFrameTimeNanos;
}
diff --git a/libs/hwui/renderthread/TimeLord.h b/libs/hwui/renderthread/TimeLord.h
index 68a0f7f..23c1e51 100644
--- a/libs/hwui/renderthread/TimeLord.h
+++ b/libs/hwui/renderthread/TimeLord.h
@@ -32,9 +32,10 @@
nsecs_t frameIntervalNanos() const { return mFrameIntervalNanos; }
// returns true if the vsync is newer, false if it was rejected for staleness
- bool vsyncReceived(nsecs_t vsync);
+ bool vsyncReceived(nsecs_t vsync, nsecs_t indendedVsync, int64_t vsyncId);
nsecs_t latestVsync() { return mFrameTimeNanos; }
nsecs_t computeFrameTimeNanos();
+ int64_t lastVsyncId() const { return mFrameVsyncId; }
private:
friend class RenderThread;
@@ -44,6 +45,8 @@
nsecs_t mFrameIntervalNanos;
nsecs_t mFrameTimeNanos;
+ nsecs_t mFrameIntendedTimeNanos;
+ int64_t mFrameVsyncId;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 4dbce92..1333b92 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -516,8 +516,9 @@
flushInfo.fFinishedContext = destroyInfo;
GrSemaphoresSubmitted submitted = bufferInfo->skSurface->flush(
SkSurface::BackendSurfaceAccess::kPresent, flushInfo);
- ALOGE_IF(!bufferInfo->skSurface->getContext(), "Surface is not backed by gpu");
- bufferInfo->skSurface->getContext()->submit();
+ GrDirectContext* context = GrAsDirectContext(bufferInfo->skSurface->recordingContext());
+ ALOGE_IF(!context, "Surface is not backed by gpu");
+ context->submit();
if (submitted == GrSemaphoresSubmitted::kYes) {
VkSemaphoreGetFdInfoKHR getFdInfo;
getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index 801cb7d..ed89c59 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -145,7 +145,8 @@
for (int i = 0; i < warmupFrameCount; i++) {
testContext.waitForVsync();
nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC);
- UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync);
+ UiFrameInfoBuilder(proxy->frameInfo())
+ .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID);
proxy->syncAndDrawFrame();
}
@@ -165,7 +166,8 @@
nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC);
{
ATRACE_NAME("UI-Draw Frame");
- UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync);
+ UiFrameInfoBuilder(proxy->frameInfo())
+ .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID);
scene->doFrame(i);
proxy->syncAndDrawFrame();
}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 0e7eaa2..c1a98ea 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -46,13 +46,13 @@
*/
interface ILocationManager
{
- Location getLastLocation(in LocationRequest request, String packageName, String attributionTag);
- void getCurrentLocation(in LocationRequest request, in ICancellationSignal cancellationSignal, in ILocationCallback callback, String packageName, String attributionTag, String listenerId);
+ Location getLastLocation(String provider, String packageName, String attributionTag);
+ void getCurrentLocation(String provider, in LocationRequest request, in ICancellationSignal cancellationSignal, in ILocationCallback callback, String packageName, String attributionTag, String listenerId);
- void registerLocationListener(in LocationRequest request, in ILocationListener listener, String packageName, String attributionTag, String listenerId);
+ void registerLocationListener(String provider, in LocationRequest request, in ILocationListener listener, String packageName, String attributionTag, String listenerId);
void unregisterLocationListener(in ILocationListener listener);
- void registerLocationPendingIntent(in LocationRequest request, in PendingIntent intent, String packageName, String attributionTag);
+ void registerLocationPendingIntent(String provider, in LocationRequest request, in PendingIntent intent, String packageName, String attributionTag);
void unregisterLocationPendingIntent(in PendingIntent intent);
void requestGeofence(in Geofence geofence, in PendingIntent intent, String packageName, String attributionTag);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 04bcbfc..d7ef241 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -670,11 +670,8 @@
public Location getLastKnownLocation(@NonNull String provider) {
Preconditions.checkArgument(provider != null, "invalid null provider");
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(
- provider, 0, 0, true);
-
try {
- return mService.getLastLocation(request, mContext.getPackageName(),
+ return mService.getLastLocation(provider, mContext.getPackageName(),
mContext.getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -682,23 +679,11 @@
}
/**
- * Asynchronously returns a single current location fix. This may activate sensors in order to
- * compute a new location, unlike {@link #getLastKnownLocation(String)}, which will only return
- * a cached fix if available. The given callback will be invoked once and only once, either with
- * a valid location fix or with a null location fix if the provider was unable to generate a
- * valid location.
+ * Asynchronously returns a single current location fix from the given provider.
*
- * <p>A client may supply an optional {@link CancellationSignal}. If this is used to cancel the
- * operation, no callback should be expected after the cancellation.
- *
- * <p>This method may return locations from the very recent past (on the order of several
- * seconds), but will never return older locations (for example, several minutes old or older).
- * Clients may rely upon the guarantee that if this method returns a location, it will represent
- * the best estimation of the location of the device in the present moment.
- *
- * <p>Clients calling this method from the background may notice that the method fails to
- * determine a valid location fix more often than while in the foreground. Background
- * applications may be throttled in their location accesses to some degree.
+ * <p>See
+ * {@link #getCurrentLocation(String, LocationRequest, CancellationSignal, Executor, Consumer)}
+ * for more information.
*
* @param provider a provider listed by {@link #getAllProviders()}
* @param cancellationSignal an optional signal that allows for cancelling this call
@@ -714,16 +699,19 @@
public void getCurrentLocation(@NonNull String provider,
@Nullable CancellationSignal cancellationSignal,
@NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Location> consumer) {
- getCurrentLocation(LocationRequest.createFromDeprecatedProvider(provider, 0, 0, true),
+ getCurrentLocation(
+ provider,
+ new LocationRequest.Builder(0).build(),
cancellationSignal, executor, consumer);
}
/**
- * Asynchronously returns a single current location fix based on the given
- * {@link LocationRequest}.
+ * Asynchronously returns a single current location fix from the given provider based on the
+ * given {@link LocationRequest}.
*
- * <p>See {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)} for more
- * information.
+ * <p>See
+ * {@link #getCurrentLocation(String, LocationRequest, CancellationSignal, Executor, Consumer)}
+ * for more information.
*
* @param locationRequest the location request containing location parameters
* @param cancellationSignal an optional signal that allows for cancelling this call
@@ -735,13 +723,66 @@
* @throws IllegalArgumentException if consumer is null
* @throws SecurityException if no suitable permission is present
* @hide
+ * @deprecated Use
+ * {@link #getCurrentLocation(String, LocationRequest, CancellationSignal, Executor, Consumer)}
+ * instead.
*/
+ @Deprecated
@SystemApi
@TestApi
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void getCurrentLocation(@NonNull LocationRequest locationRequest,
@Nullable CancellationSignal cancellationSignal,
@NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Location> consumer) {
+ Preconditions.checkArgument(locationRequest.getProvider() != null);
+ getCurrentLocation(locationRequest.getProvider(), locationRequest, cancellationSignal,
+ executor, consumer);
+ }
+
+ /**
+ * Asynchronously returns a single current location fix from the given provider based on the
+ * given {@link LocationRequest}. This may activate sensors in order to compute a new location,
+ * unlike {@link #getLastKnownLocation(String)}, which will only return a cached fix if
+ * available. The given callback will be invoked once and only once, either with a valid
+ * location or with a null location if the provider was unable to generate a valid location.
+ *
+ * <p>A client may supply an optional {@link CancellationSignal}. If this is used to cancel the
+ * operation, no callback should be expected after the cancellation.
+ *
+ * <p>This method may return locations from the very recent past (on the order of several
+ * seconds), but will never return older locations (for example, several minutes old or older).
+ * Clients may rely upon the guarantee that if this method returns a location, it will represent
+ * the best estimation of the location of the device in the present moment.
+ *
+ * <p>Clients calling this method from the background may notice that the method fails to
+ * determine a valid location fix more often than while in the foreground. Background
+ * applications may be throttled in their location accesses to some degree.
+ *
+ * The given location request may be used to provide hints on how a fresh location is computed
+ * if necessary. In particular {@link LocationRequest#getDurationMillis()} can be used to
+ * provide maximum duration allowed before failing. The system will always cap the maximum
+ * amount of time a request for current location may run to some reasonable value (less than a
+ * minute for example) before the request is failed.
+ *
+ * @param provider a provider listed by {@link #getAllProviders()}
+ * @param locationRequest the location request containing location parameters
+ * @param cancellationSignal an optional signal that allows for cancelling this call
+ * @param executor the callback will take place on this {@link Executor}
+ * @param consumer the callback invoked with either a {@link Location} or null
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if consumer is null
+ * @throws SecurityException if no suitable permission is present
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void getCurrentLocation(@NonNull String provider,
+ @NonNull LocationRequest locationRequest,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Location> consumer) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+ Preconditions.checkArgument(locationRequest != null, "invalid null location request");
+
ICancellationSignal remoteCancellationSignal = CancellationSignal.createTransport();
GetCurrentLocationTransport transport = new GetCurrentLocationTransport(executor, consumer,
remoteCancellationSignal);
@@ -752,7 +793,7 @@
}
try {
- mService.getCurrentLocation(locationRequest, remoteCancellationSignal,
+ mService.getCurrentLocation(provider, locationRequest, remoteCancellationSignal,
transport, mContext.getPackageName(), mContext.getAttributionTag(),
AppOpsManager.toReceiverId(consumer));
} catch (RemoteException e) {
@@ -782,12 +823,16 @@
public void requestSingleUpdate(
@NonNull String provider, @NonNull LocationListener listener, @Nullable Looper looper) {
Preconditions.checkArgument(provider != null, "invalid null provider");
- Preconditions.checkArgument(listener != null, "invalid null listener");
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(
- provider, 0, 0, true);
- request.setExpireIn(MAX_SINGLE_LOCATION_TIMEOUT_MS);
- requestLocationUpdates(request, listener, looper);
+ Handler handler = looper == null ? new Handler() : new Handler(looper);
+ requestLocationUpdates(
+ provider,
+ new LocationRequest.Builder(0)
+ .setMaxUpdates(1)
+ .setDurationMillis(MAX_SINGLE_LOCATION_TIMEOUT_MS)
+ .build(),
+ new HandlerExecutor(handler),
+ listener);
}
/**
@@ -814,12 +859,17 @@
@NonNull LocationListener listener,
@Nullable Looper looper) {
Preconditions.checkArgument(criteria != null, "invalid null criteria");
- Preconditions.checkArgument(listener != null, "invalid null listener");
- LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
- criteria, 0, 0, true);
- request.setExpireIn(MAX_SINGLE_LOCATION_TIMEOUT_MS);
- requestLocationUpdates(request, listener, looper);
+ Handler handler = looper == null ? new Handler() : new Handler(looper);
+ requestLocationUpdates(
+ FUSED_PROVIDER,
+ new LocationRequest.Builder(0)
+ .setQuality(criteria)
+ .setMaxUpdates(1)
+ .setDurationMillis(MAX_SINGLE_LOCATION_TIMEOUT_MS)
+ .build(),
+ new HandlerExecutor(handler),
+ listener);
}
/**
@@ -843,10 +893,13 @@
@NonNull PendingIntent pendingIntent) {
Preconditions.checkArgument(provider != null, "invalid null provider");
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(
- provider, 0, 0, true);
- request.setExpireIn(MAX_SINGLE_LOCATION_TIMEOUT_MS);
- requestLocationUpdates(request, pendingIntent);
+ requestLocationUpdates(
+ provider,
+ new LocationRequest.Builder(0)
+ .setMaxUpdates(1)
+ .setDurationMillis(MAX_SINGLE_LOCATION_TIMEOUT_MS)
+ .build(),
+ pendingIntent);
}
/**
@@ -871,61 +924,27 @@
@NonNull PendingIntent pendingIntent) {
Preconditions.checkArgument(criteria != null, "invalid null criteria");
- LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
- criteria, 0, 0, true);
- request.setExpireIn(MAX_SINGLE_LOCATION_TIMEOUT_MS);
- requestLocationUpdates(request, pendingIntent);
+ requestLocationUpdates(
+ FUSED_PROVIDER,
+ new LocationRequest.Builder(0)
+ .setQuality(criteria)
+ .setMaxUpdates(1)
+ .setDurationMillis(MAX_SINGLE_LOCATION_TIMEOUT_MS)
+ .build(),
+ pendingIntent);
}
/**
- * Register for location updates from the given provider with the given arguments. {@link
- * LocationListener} callbacks will take place on the given {@link Looper} or {@link Executor}.
- * If a null {@link Looper} is supplied, the Looper of the calling thread will be used instead.
- * Only one request can be registered for each unique listener, so any subsequent requests with
- * the same listener will overwrite all associated arguments.
+ * Register for location updates from the given provider with the given arguments, and a
+ * callback on the {@link Looper} of the calling thread.
*
- * <p> It may take a while to receive the first location update. If an immediate location is
- * required, applications may use the {@link #getLastKnownLocation(String)} method.
- *
- * <p> The location update interval can be controlled using the minimum time parameter. The
- * elapsed time between location updates will never be less than this parameter, although it may
- * be more depending on location availability and other factors. Choosing a sensible value for
- * the minimum time parameter is important to conserve battery life. Every location update
- * requires power from a variety of sensors. Select a minimum time parameter as high as possible
- * while still providing a reasonable user experience. If your application is not in the
- * foreground and showing location to the user then your application should consider switching
- * to the {@link #PASSIVE_PROVIDER} instead.
- *
- * <p> The minimum distance parameter can also be used to control the frequency of location
- * updates. If it is greater than 0 then the location provider will only send your application
- * an update when the location has changed by at least minDistance meters, AND when the minimum
- * time has elapsed. However it is more difficult for location providers to save power using the
- * minimum distance parameter, so the minimum time parameter should be the primary tool for
- * conserving battery life.
- *
- * <p> If your application wants to passively observe location updates triggered by other
- * applications, but not consume any additional power otherwise, then use the {@link
- * #PASSIVE_PROVIDER}. This provider does not turn on or modify active location providers, so
- * you do not need to be as careful about minimum time and minimum distance parameters. However,
- * if your application performs heavy work on a location update (such as network activity) then
- * you should select non-zero values for the parameters to rate-limit your update frequency in
- * the case another application enables a location provider with extremely fast updates.
- *
- * <p>In case the provider you have selected is disabled, location updates will cease, and a
- * provider availability update will be sent. As soon as the provider is enabled again, another
- * provider availability update will be sent and location updates will immediately resume.
- *
- * <p> When location callbacks are invoked, the system will hold a wakelock on your
- * application's behalf for some period of time, but not indefinitely. If your application
- * requires a long running wakelock within the location callback, you should acquire it
- * yourself.
+ * <p>See {@link #requestLocationUpdates(String, LocationRequest, Executor, LocationListener)}
+ * for more detail on how this method works.
*
* <p class="note"> Prior to Jellybean, the minTime parameter was only a hint, and some location
* provider implementations ignored it. For Jellybean and onwards however, it is mandatory for
* Android compatible devices to observe both the minTime and minDistance parameters.
*
- * <p>To unregister for location updates, use {@link #removeUpdates(LocationListener)}.
- *
* @param provider a provider listed by {@link #getAllProviders()}
* @param minTimeMs minimum time interval between location updates in milliseconds
* @param minDistanceM minimum distance between location updates in meters
@@ -939,21 +958,20 @@
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestLocationUpdates(@NonNull String provider, long minTimeMs, float minDistanceM,
@NonNull LocationListener listener) {
- Preconditions.checkArgument(provider != null, "invalid null provider");
- Preconditions.checkArgument(listener != null, "invalid null listener");
-
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(
- provider, minTimeMs, minDistanceM, false);
- requestLocationUpdates(request, listener, null);
+ requestLocationUpdates(provider, minTimeMs, minDistanceM, listener, null);
}
/**
- * Register for location updates using the named provider, and a callback on
- * the specified {@link Looper}.
+ * Register for location updates from the given provider with the given arguments, and a
+ * callback on the specified {@link Looper}.
*
- * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)}
+ * <p>See {@link #requestLocationUpdates(String, LocationRequest, Executor, LocationListener)}
* for more detail on how this method works.
*
+ * <p class="note">Prior to Jellybean, the minTime parameter was only a hint, and some location
+ * provider implementations ignored it. For Jellybean and onwards however, it is mandatory for
+ * Android compatible devices to observe both the minTime and minDistance parameters.
+ *
* @param provider a provider listed by {@link #getAllProviders()}
* @param minTimeMs minimum time interval between location updates in milliseconds
* @param minDistanceM minimum distance between location updates in meters
@@ -968,21 +986,22 @@
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestLocationUpdates(@NonNull String provider, long minTimeMs, float minDistanceM,
@NonNull LocationListener listener, @Nullable Looper looper) {
- Preconditions.checkArgument(provider != null, "invalid null provider");
- Preconditions.checkArgument(listener != null, "invalid null listener");
-
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(
- provider, minTimeMs, minDistanceM, false);
- requestLocationUpdates(request, listener, looper);
+ Handler handler = looper == null ? new Handler() : new Handler(looper);
+ requestLocationUpdates(provider, minTimeMs, minDistanceM, new HandlerExecutor(handler),
+ listener);
}
/**
* Register for location updates using the named provider, and a callback on
* the specified {@link Executor}.
*
- * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)}
+ * <p>See {@link #requestLocationUpdates(String, LocationRequest, Executor, LocationListener)}
* for more detail on how this method works.
*
+ * <p class="note">Prior to Jellybean, the minTime parameter was only a hint, and some location
+ * provider implementations ignored it. For Jellybean and onwards however, it is mandatory for
+ * Android compatible devices to observe both the minTime and minDistance parameters.
+ *
* @param provider a provider listed by {@link #getAllProviders()}
* @param minTimeMs minimum time interval between location updates in milliseconds
* @param minDistanceM minimum distance between location updates in meters
@@ -1001,16 +1020,22 @@
float minDistanceM,
@NonNull @CallbackExecutor Executor executor,
@NonNull LocationListener listener) {
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(
- provider, minTimeMs, minDistanceM, false);
- requestLocationUpdates(request, executor, listener);
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+
+ requestLocationUpdates(
+ provider,
+ new LocationRequest.Builder(minTimeMs)
+ .setMinUpdateDistanceMeters(minDistanceM)
+ .build(),
+ executor,
+ listener);
}
/**
* Register for location updates using a provider selected through the given Criteria, and a
* callback on the specified {@link Looper}.
*
- * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)}
+ * <p>See {@link #requestLocationUpdates(String, LocationRequest, Executor, LocationListener)}
* for more detail on how this method works.
*
* @param minTimeMs minimum time interval between location updates in milliseconds
@@ -1026,19 +1051,16 @@
public void requestLocationUpdates(long minTimeMs, float minDistanceM,
@NonNull Criteria criteria, @NonNull LocationListener listener,
@Nullable Looper looper) {
- Preconditions.checkArgument(criteria != null, "invalid null criteria");
- Preconditions.checkArgument(listener != null, "invalid null listener");
-
- LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
- criteria, minTimeMs, minDistanceM, false);
- requestLocationUpdates(request, listener, looper);
+ Handler handler = looper == null ? new Handler() : new Handler(looper);
+ requestLocationUpdates(minTimeMs, minDistanceM, criteria, new HandlerExecutor(handler),
+ listener);
}
/**
* Register for location updates using a provider selected through the given Criteria, and a
* callback on the specified {@link Executor}.
*
- * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)}
+ * <p>See {@link #requestLocationUpdates(String, LocationRequest, Executor, LocationListener)}
* for more detail on how this method works.
*
* @param minTimeMs minimum time interval between location updates in milliseconds
@@ -1059,23 +1081,24 @@
@NonNull Criteria criteria,
@NonNull @CallbackExecutor Executor executor,
@NonNull LocationListener listener) {
- LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
- criteria, minTimeMs, minDistanceM, false);
- requestLocationUpdates(request, executor, listener);
+ Preconditions.checkArgument(criteria != null, "invalid null criteria");
+
+ requestLocationUpdates(
+ FUSED_PROVIDER,
+ new LocationRequest.Builder(minTimeMs)
+ .setQuality(criteria)
+ .setMinUpdateDistanceMeters(minDistanceM)
+ .build(),
+ executor,
+ listener);
}
/**
* Register for location updates using the named provider, and callbacks delivered via the
* provided {@link PendingIntent}.
*
- * <p>The delivered pending intents will contain extras with the callback information. The keys
- * used for the extras are {@link #KEY_LOCATION_CHANGED} and {@link #KEY_PROVIDER_ENABLED}. See
- * the documentation for each respective extra key for information on the values.
- *
- * <p>To unregister for location updates, use {@link #removeUpdates(PendingIntent)}.
- *
- * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)}
- * for more detail on how this method works.
+ * <p>See {@link #requestLocationUpdates(String, LocationRequest, PendingIntent)} for more
+ * detail on how this method works.
*
* @param provider a provider listed by {@link #getAllProviders()}
* @param minTimeMs minimum time interval between location updates in milliseconds
@@ -1091,9 +1114,12 @@
@NonNull PendingIntent pendingIntent) {
Preconditions.checkArgument(provider != null, "invalid null provider");
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(
- provider, minTimeMs, minDistanceM, false);
- requestLocationUpdates(request, pendingIntent);
+ requestLocationUpdates(
+ provider,
+ new LocationRequest.Builder(minTimeMs)
+ .setMinUpdateDistanceMeters(minDistanceM)
+ .build(),
+ pendingIntent);
}
/**
@@ -1116,10 +1142,13 @@
public void requestLocationUpdates(long minTimeMs, float minDistanceM,
@NonNull Criteria criteria, @NonNull PendingIntent pendingIntent) {
Preconditions.checkArgument(criteria != null, "invalid null criteria");
-
- LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
- criteria, minTimeMs, minDistanceM, false);
- requestLocationUpdates(request, pendingIntent);
+ requestLocationUpdates(
+ FUSED_PROVIDER,
+ new LocationRequest.Builder(minTimeMs)
+ .setQuality(criteria)
+ .setMinUpdateDistanceMeters(minDistanceM)
+ .build(),
+ pendingIntent);
}
/**
@@ -1131,7 +1160,7 @@
* choose default low power parameters for location updates, but this is heavily discouraged,
* and an explicit LocationRequest should always be provided.
*
- * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)}
+ * <p>See {@link #requestLocationUpdates(String, LocationRequest, Executor, LocationListener)}
* for more detail on how this method works.
*
* @param locationRequest the location request containing location parameters
@@ -1143,7 +1172,10 @@
* @throws SecurityException if no suitable permission is present
*
* @hide
+ * @deprecated Use
+ * {@link #requestLocationUpdates(String, LocationRequest, Executor, LocationListener)} instead.
*/
+ @Deprecated
@SystemApi
@TestApi
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
@@ -1159,8 +1191,8 @@
* Register for location updates using a {@link LocationRequest}, and a callback on the
* specified {@link Executor}.
*
- * <p>See {@link #requestLocationUpdates(LocationRequest, LocationListener, Looper)} for more
- * detail on how this method works.
+ * <p>See {@link #requestLocationUpdates(String, LocationRequest, Executor, LocationListener)}
+ * for more detail on how this method works.
*
* @param locationRequest the location request containing location parameters
* @param executor the executor handling listener callbacks
@@ -1171,7 +1203,10 @@
* @throws SecurityException if no suitable permission is present
*
* @hide
+ * @deprecated Use
+ * {@link #requestLocationUpdates(String, LocationRequest, Executor, LocationListener)} instead.
*/
+ @Deprecated
@SystemApi
@TestApi
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
@@ -1180,8 +1215,93 @@
@NonNull @CallbackExecutor Executor executor,
@NonNull LocationListener listener) {
if (locationRequest == null) {
- locationRequest = new LocationRequest();
+ locationRequest = LocationRequest.create();
}
+ Preconditions.checkArgument(locationRequest.getProvider() != null);
+ requestLocationUpdates(locationRequest.getProvider(), locationRequest, executor, listener);
+ }
+
+ /**
+ * Register for location updates using a {@link LocationRequest}, and callbacks delivered via
+ * the provided {@link PendingIntent}.
+ *
+ * <p>See {@link #requestLocationUpdates(String, LocationRequest, PendingIntent)} for more
+ * detail on how this method works.
+ *
+ * @param locationRequest the location request containing location parameters
+ * @param pendingIntent the pending intent to send location updates
+ *
+ * @throws IllegalArgumentException if pendingIntent is null
+ * @throws SecurityException if no suitable permission is present
+ *
+ * @hide
+ * @deprecated Use {@link #requestLocationUpdates(String, LocationRequest, PendingIntent)}
+ * instead.
+ */
+ @Deprecated
+ @SystemApi
+ @TestApi
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(
+ @Nullable LocationRequest locationRequest,
+ @NonNull PendingIntent pendingIntent) {
+ if (locationRequest == null) {
+ locationRequest = LocationRequest.create();
+ }
+ Preconditions.checkArgument(locationRequest.getProvider() != null);
+ requestLocationUpdates(locationRequest.getProvider(), locationRequest, pendingIntent);
+ }
+
+ /**
+ * Register for location updates from the specified provider, using a {@link LocationRequest},
+ * and a callback on the specified {@link Executor}.
+ *
+ * <p>Only one request can be registered for each unique listener/provider pair, so any
+ * subsequent requests with the same provider and listener will overwrite all associated
+ * arguments. The same listener may be used across multiple providers with different requests
+ * for each provider.
+ *
+ * <p>It may take a while to receive the first location update. If an immediate location is
+ * required, applications may use the {@link #getLastKnownLocation(String)} method.
+ *
+ * <p>See {@link LocationRequest} documentation for an explanation of various request parameters
+ * and how they can affect the received locations.
+ *
+ * <p> If your application wants to passively observe location updates from any provider, then
+ * use the {@link #PASSIVE_PROVIDER}. This provider does not turn on or modify active location
+ * providers, so you do not need to be as careful about minimum time and minimum distance
+ * parameters. However, if your application performs heavy work on a location update (such as
+ * network activity) then you should set an explicit fastest interval on your location request
+ * in case another application enables a location provider with extremely fast updates.
+ *
+ * <p>In case the provider you have selected is disabled, location updates will cease, and a
+ * provider availability update will be sent. As soon as the provider is enabled again, another
+ * provider availability update will be sent and location updates will immediately resume.
+ *
+ * <p> When location callbacks are invoked, the system will hold a wakelock on your
+ * application's behalf for some period of time, but not indefinitely. If your application
+ * requires a long running wakelock within the location callback, you should acquire it
+ * yourself.
+ *
+ * <p>To unregister for location updates, use {@link #removeUpdates(LocationListener)}.
+ *
+ * @param provider a provider listed by {@link #getAllProviders()}
+ * @param locationRequest the location request containing location parameters
+ * @param executor the executor handling listener callbacks
+ * @param listener the listener to receive location updates
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if locationRequest is null
+ * @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if no suitable permission is present
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(@NonNull String provider,
+ @NonNull LocationRequest locationRequest,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull LocationListener listener) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+ Preconditions.checkArgument(locationRequest != null, "invalid null location request");
synchronized (sLocationListeners) {
WeakReference<LocationListenerTransport> reference = sLocationListeners.get(listener);
@@ -1198,7 +1318,7 @@
// 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(locationRequest, transport,
+ mService.registerLocationListener(provider, locationRequest, transport,
mContext.getPackageName(), mContext.getAttributionTag(),
AppOpsManager.toReceiverId(listener));
} catch (RemoteException e) {
@@ -1208,39 +1328,39 @@
}
/**
- * Register for location updates using a {@link LocationRequest}, and callbacks delivered via
- * the provided {@link PendingIntent}.
+ * Register for location updates from the specified provider, using a {@link LocationRequest},
+ * and callbacks delivered via the provided {@link PendingIntent}.
*
- * <p>See {@link #requestLocationUpdates(LocationRequest, LocationListener, Looper)} and
- * {@link #requestLocationUpdates(String, long, float, PendingIntent)} for more detail on how
- * this method works.
+ * <p>The delivered pending intents will contain extras with the callback information. The keys
+ * used for the extras are {@link #KEY_LOCATION_CHANGED} and {@link #KEY_PROVIDER_ENABLED}. See
+ * the documentation for each respective extra key for information on the values.
*
+ * <p>To unregister for location updates, use {@link #removeUpdates(PendingIntent)}.
+ *
+ * @param provider a provider listed by {@link #getAllProviders()}
* @param locationRequest the location request containing location parameters
* @param pendingIntent the pending intent to send location updates
*
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if locationRequest is null
* @throws IllegalArgumentException if pendingIntent is null
* @throws SecurityException if no suitable permission is present
- *
- * @hide
*/
- @SystemApi
- @TestApi
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
- public void requestLocationUpdates(
- @Nullable LocationRequest locationRequest,
+ public void requestLocationUpdates(@NonNull String provider,
+ @NonNull LocationRequest locationRequest,
@NonNull PendingIntent pendingIntent) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+ Preconditions.checkArgument(locationRequest != null, "invalid null location request");
Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent");
+
if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) {
Preconditions.checkArgument(pendingIntent.isTargetedToPackage(),
"pending intent must be targeted to a package");
}
- if (locationRequest == null) {
- locationRequest = new LocationRequest();
- }
-
try {
- mService.registerLocationPendingIntent(locationRequest, pendingIntent,
+ mService.registerLocationPendingIntent(provider, locationRequest, pendingIntent,
mContext.getPackageName(), mContext.getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/location/java/android/location/LocationProvider.java b/location/java/android/location/LocationProvider.java
index 52a03b6..b950604 100644
--- a/location/java/android/location/LocationProvider.java
+++ b/location/java/android/location/LocationProvider.java
@@ -92,23 +92,23 @@
}
if (criteria.getAccuracy() != Criteria.NO_REQUIREMENT &&
- criteria.getAccuracy() < properties.mAccuracy) {
+ criteria.getAccuracy() < properties.getAccuracy()) {
return false;
}
if (criteria.getPowerRequirement() != Criteria.NO_REQUIREMENT &&
- criteria.getPowerRequirement() < properties.mPowerRequirement) {
+ criteria.getPowerRequirement() < properties.getPowerRequirement()) {
return false;
}
- if (criteria.isAltitudeRequired() && !properties.mSupportsAltitude) {
+ if (criteria.isAltitudeRequired() && !properties.hasAltitudeSupport()) {
return false;
}
- if (criteria.isSpeedRequired() && !properties.mSupportsSpeed) {
+ if (criteria.isSpeedRequired() && !properties.hasSpeedSupport()) {
return false;
}
- if (criteria.isBearingRequired() && !properties.mSupportsBearing) {
+ if (criteria.isBearingRequired() && !properties.hasBearingSupport()) {
return false;
}
- if (!criteria.isCostAllowed() && properties.mHasMonetaryCost) {
+ if (!criteria.isCostAllowed() && properties.hasMonetaryCost()) {
return false;
}
return true;
@@ -119,7 +119,7 @@
* data network (e.g., the Internet), false otherwise.
*/
public boolean requiresNetwork() {
- return mProperties.mRequiresNetwork;
+ return mProperties.hasNetworkRequirement();
}
/**
@@ -128,7 +128,7 @@
* otherwise.
*/
public boolean requiresSatellite() {
- return mProperties.mRequiresSatellite;
+ return mProperties.hasSatelliteRequirement();
}
/**
@@ -137,7 +137,7 @@
* otherwise.
*/
public boolean requiresCell() {
- return mProperties.mRequiresCell;
+ return mProperties.hasCellRequirement();
}
/**
@@ -146,7 +146,7 @@
* each provider to give accurate information.
*/
public boolean hasMonetaryCost() {
- return mProperties.mHasMonetaryCost;
+ return mProperties.hasMonetaryCost();
}
/**
@@ -156,7 +156,7 @@
* should return true.
*/
public boolean supportsAltitude() {
- return mProperties.mSupportsAltitude;
+ return mProperties.hasAltitudeSupport();
}
/**
@@ -166,7 +166,7 @@
* should return true.
*/
public boolean supportsSpeed() {
- return mProperties.mSupportsSpeed;
+ return mProperties.hasSpeedSupport();
}
/**
@@ -176,7 +176,7 @@
* should return true.
*/
public boolean supportsBearing() {
- return mProperties.mSupportsBearing;
+ return mProperties.hasBearingSupport();
}
/**
@@ -186,7 +186,7 @@
* constants Criteria.POWER_REQUIREMENT_*.
*/
public int getPowerRequirement() {
- return mProperties.mPowerRequirement;
+ return mProperties.getPowerRequirement();
}
/**
@@ -197,6 +197,6 @@
* is returned.
*/
public int getAccuracy() {
- return mProperties.mAccuracy;
+ return mProperties.getAccuracy();
}
}
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 280bd05..0521b10 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -16,7 +16,12 @@
package android.location;
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+
import android.Manifest;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -26,7 +31,6 @@
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.SystemClock;
import android.os.WorkSource;
import android.util.TimeUtils;
@@ -36,73 +40,27 @@
/**
- * A data object that contains quality of service parameters for requests
- * to the {@link LocationManager}.
- *
- * <p>LocationRequest objects are used to request a quality of service
- * for location updates from the Location Manager.
- *
- * <p>For example, if your application wants high accuracy location
- * it should create a location request with {@link #setQuality} set to
- * {@link #ACCURACY_FINE} or {@link #POWER_HIGH}, and it should set
- * {@link #setInterval} to less than one second. This would be
- * appropriate for mapping applications that are showing your location
- * in real-time.
- *
- * <p>At the other extreme, if you want negligible power
- * impact, but to still receive location updates when available, then use
- * {@link #setQuality} with {@link #POWER_NONE}. With this request your
- * application will not trigger (and therefore will not receive any
- * power blame) any location updates, but will receive locations
- * triggered by other applications. This would be appropriate for
- * applications that have no firm requirement for location, but can
- * take advantage when available.
- *
- * <p>In between these two extremes is a very common use-case, where
- * applications definitely want to receive
- * updates at a specified interval, and can receive them faster when
- * available, but still want a low power impact. These applications
- * should consider {@link #POWER_LOW} combined with a faster
- * {@link #setFastestInterval} (such as 1 minute) and a slower
- * {@link #setInterval} (such as 60 minutes). They will only be assigned
- * power blame for the interval set by {@link #setInterval}, but can
- * still receive locations triggered by other applications at a rate up
- * to {@link #setFastestInterval}. This style of request is appropriate for
- * many location aware applications, including background usage. Do be
- * careful to also throttle {@link #setFastestInterval} if you perform
- * heavy-weight work after receiving an update - such as using the network.
- *
- * <p>Activities should strongly consider removing all location
- * request when entering the background, or
- * at least swap the request to a larger interval and lower quality.
- * Future version of the location manager may automatically perform background
- * throttling on behalf of applications.
- *
- * <p>Applications cannot specify the exact location sources that are
- * used by Android's <em>Fusion Engine</em>. In fact, the system
- * may have multiple location sources (providers) running and may
- * fuse the results from several sources into a single Location object.
- *
- * <p>Location requests from applications with
- * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and not
- * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} will
- * be automatically throttled to a slower interval, and the location
- * object will be obfuscated to only show a coarse level of accuracy.
- *
- * <p>All location requests are considered hints, and you may receive
- * locations that are more accurate, less accurate, and slower
- * than requested.
- *
- * @hide
+ * An encapsulation of various parameters for requesting location via {@link LocationManager}.
*/
-@SystemApi
-@TestApi
public final class LocationRequest implements Parcelable {
+
+ /**
+ * Represents a passive only request. Such a request will not trigger any active locations or
+ * power usage itself, but may receive locations generated in response to other requests.
+ *
+ * @see LocationRequest#getIntervalMillis()
+ */
+ public static final long PASSIVE_INTERVAL = Long.MAX_VALUE;
+
/**
* Used with {@link #setQuality} to request the most accurate locations available.
*
* <p>This may be up to 1 meter accuracy, although this is implementation dependent.
+ *
+ * @hide
*/
+ @SystemApi
+ @TestApi
public static final int ACCURACY_FINE = 100;
/**
@@ -111,7 +69,11 @@
* <p>Block level accuracy is considered to be about 100 meter accuracy,
* although this is implementation dependent. Using a coarse accuracy
* such as this often consumes less power.
+ *
+ * @hide
*/
+ @SystemApi
+ @TestApi
public static final int ACCURACY_BLOCK = 102;
/**
@@ -120,7 +82,11 @@
* <p>City level accuracy is considered to be about 10km accuracy,
* although this is implementation dependent. Using a coarse accuracy
* such as this often consumes less power.
+ *
+ * @hide
*/
+ @SystemApi
+ @TestApi
public static final int ACCURACY_CITY = 104;
/**
@@ -129,7 +95,12 @@
* <p>This location request will not trigger any active location requests,
* but will receive locations triggered by other applications. Your application
* will not receive any direct power blame for location work.
+ *
+ * @hide
+ * @deprecated Use {@link #PASSIVE_INTERVAL} instead.
*/
+ @SystemApi
+ @Deprecated
public static final int POWER_NONE = 200;
/**
@@ -137,66 +108,76 @@
*
* <p>This location request will avoid high power location work where
* possible.
+ *
+ * @hide
*/
+ @SystemApi
+ @TestApi
public static final int POWER_LOW = 201;
/**
* Used with {@link #setQuality} to allow high power consumption for location.
*
* <p>This location request will allow high power location work.
+ *
+ * @hide
*/
+ @SystemApi
+ @TestApi
public static final int POWER_HIGH = 203;
- private static final long DEFAULT_INTERVAL_MS = 60 * 60 * 1000; // 1 hour
- private static final double FASTEST_INTERVAL_FACTOR = 6.0; // 6x
+ private static final long IMPLICIT_MIN_UPDATE_INTERVAL = -1;
- @UnsupportedAppUsage
- private String mProvider;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@link "
+ + "LocationManager} methods to provide the provider explicitly.")
+ @Nullable private String mProvider;
private int mQuality;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@link "
+ + "LocationRequest} instead.")
private long mInterval;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- private long mFastestInterval;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- private boolean mExplicitFastestInterval;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- private long mExpireAt;
- private long mExpireIn;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- private int mNumUpdates;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- private float mSmallestDisplacement;
- @UnsupportedAppUsage
+ private long mMinUpdateIntervalMillis;
+ private long mExpireAtRealtimeMillis;
+ private long mDurationMillis;
+ private int mMaxUpdates;
+ private float mMinUpdateDistanceMeters;
private boolean mHideFromAppOps;
private boolean mLocationSettingsIgnored;
- private boolean mLowPowerMode;
- @UnsupportedAppUsage
+ private boolean mLowPower;
private @Nullable WorkSource mWorkSource;
/**
- * Create a location request with default parameters.
- *
- * <p>Default parameters are for a low power, slowly updated location.
- * It can then be adjusted as required by the applications before passing
- * to the {@link LocationManager}
- *
- * @return a new location request
+ * @hide
+ * @deprecated Use the Builder to construct new LocationRequests.
*/
+ @SystemApi
+ @Deprecated
@NonNull
public static LocationRequest create() {
- return new LocationRequest();
+ // 60 minutes is the default legacy interval
+ return new LocationRequest.Builder(60 * 60 * 1000)
+ .setQuality(POWER_LOW)
+ .build();
}
- /** @hide */
+ /**
+ * @hide
+ * @deprecated Use the Builder to construct new LocationRequests.
+ */
@SystemApi
+ @Deprecated
@NonNull
- public static LocationRequest createFromDeprecatedProvider(
- @NonNull String provider, long minTime, float minDistance, boolean singleShot) {
+ public static LocationRequest createFromDeprecatedProvider(@NonNull String provider,
+ long intervalMillis, float minUpdateDistanceMeters, boolean singleShot) {
Preconditions.checkArgument(provider != null, "invalid null provider");
- if (minTime < 0) minTime = 0;
- if (minDistance < 0) minDistance = 0;
+ if (intervalMillis < 0) {
+ intervalMillis = 0;
+ } else if (intervalMillis == PASSIVE_INTERVAL) {
+ intervalMillis = Long.MAX_VALUE - 1;
+ }
+ if (minUpdateDistanceMeters < 0) {
+ minUpdateDistanceMeters = 0;
+ }
int quality;
if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
@@ -207,235 +188,387 @@
quality = POWER_LOW;
}
- LocationRequest request = new LocationRequest()
+ return new LocationRequest.Builder(intervalMillis)
+ .setMinUpdateIntervalMillis(intervalMillis)
+ .setMinUpdateDistanceMeters(minUpdateDistanceMeters)
+ .setMaxUpdates(singleShot ? 1 : Integer.MAX_VALUE)
+ .build()
.setProvider(provider)
- .setQuality(quality)
- .setInterval(minTime)
- .setFastestInterval(minTime)
- .setSmallestDisplacement(minDistance);
- if (singleShot) request.setNumUpdates(1);
- return request;
+ .setQuality(quality);
}
- /** @hide */
+ /**
+ * @hide
+ * @deprecated Use the Builder to construct new LocationRequests.
+ */
@SystemApi
+ @Deprecated
@NonNull
- public static LocationRequest createFromDeprecatedCriteria(
- @NonNull Criteria criteria, long minTime, float minDistance, boolean singleShot) {
+ public static LocationRequest createFromDeprecatedCriteria(@NonNull Criteria criteria,
+ long intervalMillis, float minUpdateDistanceMeters, boolean singleShot) {
Preconditions.checkArgument(criteria != null, "invalid null criteria");
- if (minTime < 0) minTime = 0;
- if (minDistance < 0) minDistance = 0;
-
- int quality;
- switch (criteria.getAccuracy()) {
- case Criteria.ACCURACY_COARSE:
- quality = ACCURACY_BLOCK;
- break;
- case Criteria.ACCURACY_FINE:
- quality = ACCURACY_FINE;
- break;
- default: {
- if (criteria.getPowerRequirement() == Criteria.POWER_HIGH) {
- quality = POWER_HIGH;
- } else {
- quality = POWER_LOW;
- }
- }
+ if (intervalMillis < 0) {
+ intervalMillis = 0;
+ } else if (intervalMillis == PASSIVE_INTERVAL) {
+ intervalMillis = Long.MAX_VALUE - 1;
+ }
+ if (minUpdateDistanceMeters < 0) {
+ minUpdateDistanceMeters = 0;
}
- LocationRequest request = new LocationRequest()
- .setQuality(quality)
- .setInterval(minTime)
- .setFastestInterval(minTime)
- .setSmallestDisplacement(minDistance);
- if (singleShot) request.setNumUpdates(1);
- return request;
- }
-
- /** @hide */
- public LocationRequest() {
- this(
- /* provider= */ LocationManager.FUSED_PROVIDER,
- /* quality= */ POWER_LOW,
- /* interval= */ DEFAULT_INTERVAL_MS,
- /* fastestInterval= */ (long) (DEFAULT_INTERVAL_MS / FASTEST_INTERVAL_FACTOR),
- /* explicitFastestInterval= */ false,
- /* expireAt= */ Long.MAX_VALUE,
- /* expireIn= */ Long.MAX_VALUE,
- /* numUpdates= */ Integer.MAX_VALUE,
- /* smallestDisplacement= */ 0,
- /* hideFromAppOps= */ false,
- /* locationSettingsIgnored= */ false,
- /* lowPowerMode= */ false,
- /* workSource= */ null);
- }
-
- /** @hide */
- public LocationRequest(LocationRequest src) {
- this(
- src.mProvider,
- src.mQuality,
- src.mInterval,
- src.mFastestInterval,
- src.mExplicitFastestInterval,
- src.mExpireAt,
- src.mExpireIn,
- src.mNumUpdates,
- src.mSmallestDisplacement,
- src.mHideFromAppOps,
- src.mLocationSettingsIgnored,
- src.mLowPowerMode,
- src.mWorkSource);
+ return new LocationRequest.Builder(intervalMillis)
+ .setQuality(criteria)
+ .setMinUpdateIntervalMillis(intervalMillis)
+ .setMinUpdateDistanceMeters(minUpdateDistanceMeters)
+ .setMaxUpdates(singleShot ? 1 : Integer.MAX_VALUE)
+ .build();
}
private LocationRequest(
- @NonNull String provider,
+ @Nullable String provider,
+ long intervalMillis,
int quality,
- long intervalMs,
- long fastestIntervalMs,
- boolean explicitFastestInterval,
- long expireAt,
- long expireInMs,
- int numUpdates,
- float smallestDisplacementM,
- boolean hideFromAppOps,
+ long expireAtRealtimeMillis,
+ long durationMillis,
+ int maxUpdates,
+ long minUpdateIntervalMillis,
+ float minUpdateDistanceMeters,
+ boolean hiddenFromAppOps,
boolean locationSettingsIgnored,
- boolean lowPowerMode,
- WorkSource workSource) {
- Preconditions.checkArgument(provider != null, "invalid provider: null");
- checkQuality(quality);
+ boolean lowPower,
+ @Nullable WorkSource workSource) {
+ Preconditions.checkArgument(intervalMillis != PASSIVE_INTERVAL || quality == POWER_NONE);
+ Preconditions.checkArgument(minUpdateIntervalMillis <= intervalMillis);
mProvider = provider;
+ mInterval = intervalMillis;
mQuality = quality;
- mInterval = intervalMs;
- mFastestInterval = fastestIntervalMs;
- mExplicitFastestInterval = explicitFastestInterval;
- mExpireAt = expireAt;
- mExpireIn = expireInMs;
- mNumUpdates = numUpdates;
- mSmallestDisplacement = Preconditions.checkArgumentInRange(smallestDisplacementM, 0,
- Float.MAX_VALUE, "smallestDisplacementM");
- mHideFromAppOps = hideFromAppOps;
- mLowPowerMode = lowPowerMode;
+ mMinUpdateIntervalMillis = minUpdateIntervalMillis;
+ mExpireAtRealtimeMillis = expireAtRealtimeMillis;
+ mDurationMillis = durationMillis;
+ mMaxUpdates = maxUpdates;
+ mMinUpdateDistanceMeters = minUpdateDistanceMeters;
+ mHideFromAppOps = hiddenFromAppOps;
+ mLowPower = lowPower;
mLocationSettingsIgnored = locationSettingsIgnored;
mWorkSource = workSource;
}
/**
- * Set the quality of the request.
- *
- * <p>Use with a accuracy constant such as {@link #ACCURACY_FINE}, or a power
- * constant such as {@link #POWER_LOW}. You cannot request both accuracy and
- * power, only one or the other can be specified. The system will then
- * maximize accuracy or minimize power as appropriate.
- *
- * <p>The quality of the request is a strong hint to the system for which
- * location sources to use. For example, {@link #ACCURACY_FINE} is more likely
- * to use GPS, and {@link #POWER_LOW} is more likely to use WIFI & Cell tower
- * positioning, but it also depends on many other factors (such as which sources
- * are available) and is implementation dependent.
- *
- * <p>{@link #setQuality} and {@link #setInterval} are the most important parameters
- * on a location request.
- *
- * @param quality an accuracy or power constant
- * @return the same object, so that setters can be chained
- * @throws IllegalArgumentException if the quality constant is not valid
+ * @hide
+ * @deprecated LocationRequests should be treated as immutable.
*/
- public @NonNull LocationRequest setQuality(int quality) {
- checkQuality(quality);
- mQuality = quality;
+ @SystemApi
+ @Deprecated
+ public @NonNull LocationRequest setProvider(@NonNull String provider) {
+ Preconditions.checkArgument(provider != null);
+ mProvider = provider;
return this;
}
/**
- * Get the quality of the request.
- *
- * @return an accuracy or power constant
+ * @hide
+ * @deprecated Providers are no longer an explicit part of a location request.
*/
- public int getQuality() {
- return mQuality;
+ @SystemApi
+ @Deprecated
+ public @NonNull String getProvider() {
+ return mProvider != null ? mProvider : LocationManager.FUSED_PROVIDER;
}
/**
- * Set the desired interval for active location updates, in milliseconds.
- *
- * <p>The location manager will actively try to obtain location updates
- * for your application at this interval, so it has a
- * direct influence on the amount of power used by your application.
- * Choose your interval wisely.
- *
- * <p>This interval is inexact. You may not receive updates at all (if
- * no location sources are available), or you may receive them
- * slower than requested. You may also receive them faster than
- * requested (if other applications are requesting location at a
- * faster interval). The fastest rate that you will receive
- * updates can be controlled with {@link #setFastestInterval}.
- *
- * <p>Applications with only the coarse location permission may have their
- * interval silently throttled.
- *
- * <p>An interval of 0 is allowed, but not recommended, since
- * location updates may be extremely fast on future implementations.
- *
- * <p>{@link #setQuality} and {@link #setInterval} are the most important parameters
- * on a location request.
- *
- * @param millis desired interval in millisecond, inexact
- * @return the same object, so that setters can be chained
- * @throws IllegalArgumentException if the interval is less than zero
+ * @hide
+ * @deprecated LocationRequests should be treated as immutable.
*/
+ @SystemApi
+ @Deprecated
+ public @NonNull LocationRequest setQuality(int quality) {
+ mQuality = Builder.checkQuality(quality, true);
+ return this;
+ }
+
+ /**
+ * Returns the quality of the location request.
+ *
+ * @return the quality of the location request
+ *
+ * @hide
+ */
+ @SystemApi
+ public int getQuality() {
+ if (mInterval == PASSIVE_INTERVAL) {
+ return POWER_NONE;
+ } else {
+ return mQuality;
+ }
+ }
+
+ /**
+ * @hide
+ * @deprecated LocationRequests should be treated as immutable.
+ */
+ @SystemApi
+ @Deprecated
public @NonNull LocationRequest setInterval(long millis) {
- Preconditions.checkArgument(millis >= 0, "invalid interval: + millis");
+ Preconditions.checkArgument(millis >= 0);
+
+ // legacy clients don't know about the passive interval
+ if (millis == PASSIVE_INTERVAL) {
+ millis = Long.MAX_VALUE - 1;
+ }
+
mInterval = millis;
- if (!mExplicitFastestInterval) {
- mFastestInterval = (long) (mInterval / FASTEST_INTERVAL_FACTOR);
+ if (mMinUpdateIntervalMillis > mInterval) {
+ mMinUpdateIntervalMillis = mInterval;
}
return this;
}
/**
- * Get the desired interval of this request, in milliseconds.
- *
- * @return desired interval in milliseconds, inexact
+ * @hide
+ * @deprecated Use {@link #getIntervalMillis()} instead.
*/
+ @SystemApi
+ @Deprecated
public long getInterval() {
+ return getIntervalMillis();
+ }
+
+ /**
+ * Returns the desired interval of location updates, or {@link #PASSIVE_INTERVAL} if this is a
+ * passive, no power request. A passive request will not actively generate location updates
+ * (and thus will not be power blamed for location), but may receive location updates generated
+ * as a result of other location requests. A passive request must always have an explicit
+ * minimum update interval set.
+ *
+ * <p>Locations may be available at a faster interval than specified here, see
+ * {@link #getMinUpdateIntervalMillis()} for the behavior in that case.
+ *
+ * @return the desired interval of location updates
+ */
+ public @IntRange(from = 0) long getIntervalMillis() {
return mInterval;
}
-
/**
- * Requests the GNSS chipset to run in a low power mode and make strong tradeoffs to
- * substantially restrict power.
- *
- * <p>In this mode, the GNSS chipset will not, on average, run power hungry operations like RF &
- * signal searches for more than one second per interval (specified by
- * {@link #setInterval(long)}).
- *
- * @param enabled Enable or disable low power mode
- * @return the same object, so that setters can be chained
+ * @hide
+ * @deprecated LocationRequests should be treated as immutable.
*/
- public @NonNull LocationRequest setLowPowerMode(boolean enabled) {
- mLowPowerMode = enabled;
+ @SystemApi
+ @Deprecated
+ public @NonNull LocationRequest setFastestInterval(long millis) {
+ Preconditions.checkArgument(millis >= 0);
+ mMinUpdateIntervalMillis = millis;
return this;
}
/**
- * Returns true if low power mode is enabled.
+ * @hide
+ * @deprecated Use {@link #getMinUpdateIntervalMillis()} instead.
*/
- public boolean isLowPowerMode() {
- return mLowPowerMode;
+ @SystemApi
+ @Deprecated
+ public long getFastestInterval() {
+ return getMinUpdateIntervalMillis();
}
/**
- * Requests that user location settings be ignored in order to satisfy this request. This API
- * is only for use in extremely rare scenarios where it is appropriate to ignore user location
- * settings, such as a user initiated emergency (dialing 911 for instance).
- *
- * @param locationSettingsIgnored Whether to ignore location settings
- * @return the same object, so that setters can be chained
+ * @hide
+ * @deprecated LocationRequests should be treated as immutable.
*/
+ @SystemApi
+ @Deprecated
+ public @NonNull LocationRequest setExpireAt(long millis) {
+ mExpireAtRealtimeMillis = max(millis, 0);
+ return this;
+ }
+
+ /**
+ * @hide
+ * @deprecated Prefer {@link #getDurationMillis()} where possible.
+ */
+ @SystemApi
+ @Deprecated
+ public long getExpireAt() {
+ return mExpireAtRealtimeMillis;
+ }
+
+ /**
+ * @hide
+ * @deprecated LocationRequests should be treated as immutable.
+ */
+ @SystemApi
+ @Deprecated
+ public @NonNull LocationRequest setExpireIn(long millis) {
+ mDurationMillis = millis;
+ return this;
+ }
+
+ /**
+ * @hide
+ * @deprecated Use {@link #getDurationMillis()} instead.
+ */
+ @SystemApi
+ @Deprecated
+ public long getExpireIn() {
+ return getDurationMillis();
+ }
+
+ /**
+ * Returns the duration for which location will be provided before the request is automatically
+ * removed. A duration of <code>Long.MAX_VALUE</code> represents an unlimited duration.
+ *
+ * @return the duration for which location will be provided
+ */
+ public @IntRange(from = 1) long getDurationMillis() {
+ return mDurationMillis;
+ }
+
+ /**
+ * @hide
+ */
+ public long getExpirationRealtimeMs(long startRealtimeMs) {
+ long expirationRealtimeMs;
+ // Check for > Long.MAX_VALUE overflow (elapsedRealtime > 0):
+ if (mDurationMillis > Long.MAX_VALUE - startRealtimeMs) {
+ expirationRealtimeMs = Long.MAX_VALUE;
+ } else {
+ expirationRealtimeMs = startRealtimeMs + mDurationMillis;
+ }
+ return min(expirationRealtimeMs, mExpireAtRealtimeMillis);
+ }
+
+ /**
+ * @hide
+ * @deprecated LocationRequests should be treated as immutable.
+ */
+ @SystemApi
+ @Deprecated
+ public @NonNull LocationRequest setNumUpdates(int numUpdates) {
+ if (numUpdates <= 0) {
+ throw new IllegalArgumentException(
+ "invalid numUpdates: " + numUpdates);
+ }
+ mMaxUpdates = numUpdates;
+ return this;
+ }
+
+ /**
+ * @hide
+ * @deprecated Use {@link #getMaxUpdates()} instead.
+ */
+ @SystemApi
+ @Deprecated
+ public int getNumUpdates() {
+ return getMaxUpdates();
+ }
+
+ /**
+ * Returns the maximum number of location updates for this request before the request is
+ * automatically removed. A max updates value of <code>Integer.MAX_VALUE</code> represents an
+ * unlimited number of updates.
+ */
+ public @IntRange(from = 1, to = Integer.MAX_VALUE) int getMaxUpdates() {
+ return mMaxUpdates;
+ }
+
+ /**
+ * Returns the minimum update interval. If location updates are available faster than the
+ * request interval then locations will only be updated if the minimum update interval has
+ * expired since the last location update.
+ *
+ * <p class=note><strong>Note:</strong> Some allowance for jitter is already built into the
+ * minimum update interval, so you need not worry about updates blocked simply because they
+ * arrived a fraction of a second earlier than expected.
+ *
+ * @return the minimum update interval
+ */
+ public @IntRange(from = 0) long getMinUpdateIntervalMillis() {
+ if (mMinUpdateIntervalMillis == IMPLICIT_MIN_UPDATE_INTERVAL) {
+ return mInterval;
+ } else {
+ // the min is only necessary in case someone use a deprecated function to mess with the
+ // interval or min update interval
+ return min(mMinUpdateIntervalMillis, mInterval);
+ }
+ }
+
+ /**
+ * @hide
+ * @deprecated LocationRequests should be treated as immutable.
+ */
+ @SystemApi
+ @Deprecated
+ public @NonNull LocationRequest setSmallestDisplacement(float minDisplacementMeters) {
+ mMinUpdateDistanceMeters = Preconditions.checkArgumentInRange(minDisplacementMeters, 0,
+ Float.MAX_VALUE, "minDisplacementMeters");
+ return this;
+ }
+
+ /**
+ * @hide
+ * @deprecated Use {@link #getMinUpdateDistanceMeters()} instead.
+ */
+ @SystemApi
+ @Deprecated
+ public float getSmallestDisplacement() {
+ return getMinUpdateDistanceMeters();
+ }
+
+ /**
+ * Returns the minimum distance between location updates. If a potential location update is
+ * closer to the last location update than the minimum update distance, then the potential
+ * location update will not occur. A value of 0 meters implies that no location update will ever
+ * be rejected due to failing this constraint.
+ *
+ * @return the minimum distance between location updates
+ */
+ public @FloatRange(from = 0, to = Float.MAX_VALUE) float getMinUpdateDistanceMeters() {
+ return mMinUpdateDistanceMeters;
+ }
+
+ /**
+ * @hide
+ * @deprecated LocationRequests should be treated as immutable.
+ */
+ @SystemApi
+ @Deprecated
+ public void setHideFromAppOps(boolean hiddenFromAppOps) {
+ mHideFromAppOps = hiddenFromAppOps;
+ }
+
+ /**
+ * @hide
+ * @deprecated Use {@link #isHiddenFromAppOps()} instead.
+ */
+ @SystemApi
+ @Deprecated
+ public boolean getHideFromAppOps() {
+ return isHiddenFromAppOps();
+ }
+
+ /**
+ * Returns true if this request should be ignored while updating app ops with location usage.
+ * This implies that someone else (usually the creator of the location request) is responsible
+ * for updating app ops.
+ *
+ * @return true if this request should be ignored while updating app ops with location usage
+ *
+ * @hide
+ */
+ @TestApi
+ @SystemApi
+ public boolean isHiddenFromAppOps() {
+ return mHideFromAppOps;
+ }
+
+ /**
+ * @hide
+ * @deprecated LocationRequests should be treated as immutable.
+ */
+ @SystemApi
+ @Deprecated
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
public @NonNull LocationRequest setLocationSettingsIgnored(boolean locationSettingsIgnored) {
mLocationSettingsIgnored = locationSettingsIgnored;
@@ -443,258 +576,79 @@
}
/**
- * Returns true if location settings will be ignored in order to satisfy this request.
+ * Returns true if location settings, throttling, background location limits, and any other
+ * possible limiting factors will be ignored in order to satisfy this request.
+ *
+ * @return true if all limiting factors will be ignored to satisfy this request
+ *
+ * @hide
*/
+ @TestApi
+ @SystemApi
public boolean isLocationSettingsIgnored() {
return mLocationSettingsIgnored;
}
/**
- * Explicitly set the fastest interval for location updates, in
- * milliseconds.
- *
- * <p>This controls the fastest rate at which your application will
- * receive location updates, which might be faster than
- * {@link #setInterval} in some situations (for example, if other
- * applications are triggering location updates).
- *
- * <p>This allows your application to passively acquire locations
- * at a rate faster than it actively acquires locations, saving power.
- *
- * <p>Unlike {@link #setInterval}, this parameter is exact. Your
- * application will never receive updates faster than this value.
- *
- * <p>If you don't call this method, a fastest interval
- * will be selected for you. It will be a value faster than your
- * active interval ({@link #setInterval}).
- *
- * <p>An interval of 0 is allowed, but not recommended, since
- * location updates may be extremely fast on future implementations.
- *
- * <p>If the fastest interval set is slower than {@link #setInterval},
- * then your effective fastest interval is {@link #setInterval}.
- *
- * @param millis fastest interval for updates in milliseconds
- * @return the same object, so that setters can be chained
- * @throws IllegalArgumentException if the interval is less than zero
+ * @hide
+ * @deprecated LocationRequests should be treated as immutable.
*/
- public @NonNull LocationRequest setFastestInterval(long millis) {
- Preconditions.checkArgument(millis >= 0, "invalid interval: + millis");
- mExplicitFastestInterval = true;
- mFastestInterval = millis;
+ @SystemApi
+ @Deprecated
+ public @NonNull LocationRequest setLowPowerMode(boolean enabled) {
+ mLowPower = enabled;
return this;
}
/**
- * Get the fastest interval of this request in milliseconds. The system will never provide
- * location updates faster than the minimum of the fastest interval and {@link #getInterval}.
- *
- * @return fastest interval in milliseconds
- */
- public long getFastestInterval() {
- return mFastestInterval;
- }
-
- /**
- * Set the expiration time of this request in milliseconds of realtime since boot. Values in the
- * past are allowed, but indicate that the request has already expired. The location manager
- * will automatically stop updates after the request expires.
- *
- * @param millis expiration time of request in milliseconds since boot
- * @return the same object, so that setters can be chained
- * @see SystemClock#elapsedRealtime()
- * @deprecated Prefer {@link #setExpireIn(long)}.
+ * @hide
+ * @deprecated Use {@link #isLowPower()} instead.
*/
@Deprecated
- public @NonNull LocationRequest setExpireAt(long millis) {
- mExpireAt = Math.max(millis, 0);
- return this;
+ @SystemApi
+ public boolean isLowPowerMode() {
+ return isLowPower();
}
/**
- * Get the request expiration time in milliseconds of realtime since boot.
+ * Returns true if extreme trade-offs should be made to save power for this request. This
+ * usually involves specialized hardware modes which can greatly affect the quality of
+ * locations.
*
- * @return request expiration time in milliseconds since boot
- * @see SystemClock#elapsedRealtime()
- * @deprecated Prefer {@link #getExpireIn()}.
- */
- @Deprecated
- public long getExpireAt() {
- return mExpireAt;
- }
-
- /**
- * Set the duration of this request in milliseconds of realtime. Values less than 0 are allowed,
- * but indicate that the request has already expired. The location manager will automatically
- * stop updates after the request expires.
- *
- * @param millis duration of request in milliseconds
- * @return the same object, so that setters can be chained
- * @see SystemClock#elapsedRealtime()
- */
- public @NonNull LocationRequest setExpireIn(long millis) {
- mExpireIn = millis;
- return this;
- }
-
- /**
- * Get the request expiration duration in milliseconds of realtime.
- *
- * @return request expiration duration in milliseconds
- * @see SystemClock#elapsedRealtime()
- */
- public long getExpireIn() {
- return mExpireIn;
- }
-
- /**
- * Returns the realtime at which this request expires, taking into account both
- * {@link #setExpireAt(long)} and {@link #setExpireIn(long)} relative to the given realtime.
+ * @return true if extreme trade-offs should be made to save power for this request
*
* @hide
*/
- public long getExpirationRealtimeMs(long startRealtimeMs) {
- long expirationRealtimeMs;
- // Check for > Long.MAX_VALUE overflow (elapsedRealtime > 0):
- if (mExpireIn > Long.MAX_VALUE - startRealtimeMs) {
- expirationRealtimeMs = Long.MAX_VALUE;
- } else {
- expirationRealtimeMs = startRealtimeMs + mExpireIn;
- }
- return Math.min(expirationRealtimeMs, mExpireAt);
+ @TestApi
+ @SystemApi
+ public boolean isLowPower() {
+ return mLowPower;
}
/**
- * Set the number of location updates.
- *
- * <p>By default locations are continuously updated until the request is explicitly
- * removed, however you can optionally request a set number of updates.
- * For example, if your application only needs a single fresh location,
- * then call this method with a value of 1 before passing the request
- * to the location manager.
- *
- * @param numUpdates the number of location updates requested
- * @return the same object, so that setters can be chained
- * @throws IllegalArgumentException if numUpdates is 0 or less
- */
- public @NonNull LocationRequest setNumUpdates(int numUpdates) {
- if (numUpdates <= 0) {
- throw new IllegalArgumentException(
- "invalid numUpdates: " + numUpdates);
- }
- mNumUpdates = numUpdates;
- return this;
- }
-
- /**
- * Get the number of updates requested.
- *
- * <p>By default this is {@link Integer#MAX_VALUE}, which indicates that
- * locations are updated until the request is explicitly removed.
- *
- * @return number of updates
- */
- public int getNumUpdates() {
- return mNumUpdates;
- }
-
- /** @hide */
- public void decrementNumUpdates() {
- if (mNumUpdates != Integer.MAX_VALUE) {
- mNumUpdates--;
- }
- if (mNumUpdates < 0) {
- mNumUpdates = 0;
- }
- }
-
- /** Sets the provider to use for this location request. */
- public @NonNull LocationRequest setProvider(@NonNull String provider) {
- Preconditions.checkArgument(provider != null, "invalid provider: null");
- mProvider = provider;
- return this;
- }
-
- /** @hide */
- @SystemApi
- public @NonNull String getProvider() {
- return mProvider;
- }
-
- /** @hide */
- @SystemApi
- public @NonNull LocationRequest setSmallestDisplacement(float smallestDisplacementM) {
- mSmallestDisplacement = Preconditions.checkArgumentInRange(smallestDisplacementM, 0,
- Float.MAX_VALUE, "smallestDisplacementM");
- return this;
- }
-
- /** @hide */
- @SystemApi
- public float getSmallestDisplacement() {
- return mSmallestDisplacement;
- }
-
- /**
- * Sets the WorkSource to use for power blaming of this location request.
- *
- * <p>No permissions are required to make this call, however the LocationManager
- * will throw a SecurityException when requesting location updates if the caller
- * doesn't have the {@link android.Manifest.permission#UPDATE_DEVICE_STATS} permission.
- *
- * @param workSource WorkSource defining power blame for this location request.
* @hide
+ * @deprecated LocationRequests should be treated as immutable.
*/
@SystemApi
+ @Deprecated
public void setWorkSource(@Nullable WorkSource workSource) {
mWorkSource = workSource;
}
- /** @hide */
+ /**
+ * Returns the work source used for power blame for this request. If null, the system is free to
+ * assign power blame as it deems most appropriate.
+ *
+ * @return the work source used for power blame for this request
+ *
+ * @hide
+ */
+ @TestApi
@SystemApi
public @Nullable WorkSource getWorkSource() {
return mWorkSource;
}
- /**
- * Sets whether or not this location request should be hidden from AppOps.
- *
- * <p>Hiding a location request from AppOps will remove user visibility in the UI as to this
- * request's existence. It does not affect power blaming in the Battery page.
- *
- * <p>No permissions are required to make this call, however the LocationManager
- * will throw a SecurityException when requesting location updates if the caller
- * doesn't have the {@link android.Manifest.permission#UPDATE_APP_OPS_STATS} permission.
- *
- * @param hideFromAppOps If true AppOps won't keep track of this location request.
- * @hide
- * @see android.app.AppOpsManager
- */
- @SystemApi
- public void setHideFromAppOps(boolean hideFromAppOps) {
- mHideFromAppOps = hideFromAppOps;
- }
-
- /** @hide */
- @SystemApi
- public boolean getHideFromAppOps() {
- return mHideFromAppOps;
- }
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- private static void checkQuality(int quality) {
- switch (quality) {
- case ACCURACY_FINE:
- case ACCURACY_BLOCK:
- case ACCURACY_CITY:
- case POWER_NONE:
- case POWER_LOW:
- case POWER_HIGH:
- break;
- default:
- throw new IllegalArgumentException("invalid quality: " + quality);
- }
- }
public static final @NonNull Parcelable.Creator<LocationRequest> CREATOR =
new Parcelable.Creator<LocationRequest>() {
@@ -702,17 +656,16 @@
public LocationRequest createFromParcel(Parcel in) {
return new LocationRequest(
/* provider= */ in.readString(),
+ /* intervalMillis= */ in.readLong(),
/* quality= */ in.readInt(),
- /* interval= */ in.readLong(),
- /* fastestInterval= */ in.readLong(),
- /* explicitFastestInterval= */ in.readBoolean(),
- /* expireAt= */ in.readLong(),
- /* expireIn= */ in.readLong(),
- /* numUpdates= */ in.readInt(),
- /* smallestDisplacement= */ in.readFloat(),
- /* hideFromAppOps= */ in.readBoolean(),
+ /* expireAtRealtimeMillis= */ in.readLong(),
+ /* durationMillis= */ in.readLong(),
+ /* maxUpdates= */ in.readInt(),
+ /* minUpdateIntervalMillis= */ in.readLong(),
+ /* minUpdateDistanceMeters= */ in.readFloat(),
+ /* hiddenFromAppOps= */ in.readBoolean(),
/* locationSettingsIgnored= */ in.readBoolean(),
- /* lowPowerMode= */ in.readBoolean(),
+ /* lowPower= */ in.readBoolean(),
/* workSource= */ in.readTypedObject(WorkSource.CREATOR));
}
@@ -728,24 +681,101 @@
}
@Override
- public void writeToParcel(Parcel parcel, int flags) {
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeString(mProvider);
- parcel.writeInt(mQuality);
parcel.writeLong(mInterval);
- parcel.writeLong(mFastestInterval);
- parcel.writeBoolean(mExplicitFastestInterval);
- parcel.writeLong(mExpireAt);
- parcel.writeLong(mExpireIn);
- parcel.writeInt(mNumUpdates);
- parcel.writeFloat(mSmallestDisplacement);
+ parcel.writeInt(mQuality);
+ parcel.writeLong(mExpireAtRealtimeMillis);
+ parcel.writeLong(mDurationMillis);
+ parcel.writeInt(mMaxUpdates);
+ parcel.writeLong(mMinUpdateIntervalMillis);
+ parcel.writeFloat(mMinUpdateDistanceMeters);
parcel.writeBoolean(mHideFromAppOps);
parcel.writeBoolean(mLocationSettingsIgnored);
- parcel.writeBoolean(mLowPowerMode);
+ parcel.writeBoolean(mLowPower);
parcel.writeTypedObject(mWorkSource, 0);
}
- /** @hide */
- public static String qualityToString(int quality) {
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ LocationRequest that = (LocationRequest) o;
+ return mInterval == that.mInterval
+ && mQuality == that.mQuality
+ && mExpireAtRealtimeMillis == that.mExpireAtRealtimeMillis
+ && mDurationMillis == that.mDurationMillis
+ && mMaxUpdates == that.mMaxUpdates
+ && mMinUpdateIntervalMillis == that.mMinUpdateIntervalMillis
+ && Float.compare(that.mMinUpdateDistanceMeters, mMinUpdateDistanceMeters) == 0
+ && mHideFromAppOps == that.mHideFromAppOps
+ && mLocationSettingsIgnored == that.mLocationSettingsIgnored
+ && mLowPower == that.mLowPower
+ && Objects.equals(mProvider, that.mProvider)
+ && Objects.equals(mWorkSource, that.mWorkSource);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mProvider, mInterval, mWorkSource);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ s.append("Request[");
+ if (mProvider != null) {
+ s.append(mProvider).append(" ");
+ }
+ if (mQuality != POWER_NONE && mQuality != ACCURACY_BLOCK) {
+ s.append(qualityToString(mQuality)).append(" ");
+ }
+ if (mInterval != PASSIVE_INTERVAL) {
+ s.append("interval=");
+ TimeUtils.formatDuration(mInterval, s);
+ } else {
+ s.append("PASSIVE");
+ }
+ if (mExpireAtRealtimeMillis != Long.MAX_VALUE) {
+ s.append(" expireAt=").append(TimeUtils.formatRealtime(mExpireAtRealtimeMillis));
+ }
+ if (mDurationMillis != Long.MAX_VALUE) {
+ s.append(" duration=");
+ TimeUtils.formatDuration(mDurationMillis, s);
+ }
+ if (mMaxUpdates != Integer.MAX_VALUE) {
+ s.append(" maxUpdates=").append(mMaxUpdates);
+ }
+ if (mMinUpdateIntervalMillis < mInterval) {
+ s.append(" minUpdateInterval=");
+ TimeUtils.formatDuration(mMinUpdateIntervalMillis, s);
+ }
+ if (mMinUpdateDistanceMeters > 0.0) {
+ s.append(" minUpdateDistance=").append(mMinUpdateDistanceMeters);
+ }
+ if (mLowPower) {
+ s.append(" lowPower");
+ }
+ if (mHideFromAppOps) {
+ s.append(" hiddenFromAppOps");
+ }
+ if (mLocationSettingsIgnored) {
+ s.append(" locationSettingsIgnored");
+ }
+ if (mWorkSource != null) {
+ s.append(" ").append(mWorkSource);
+ }
+ s.append(']');
+ return s.toString();
+ }
+
+ private static String qualityToString(int quality) {
switch (quality) {
case ACCURACY_FINE:
return "ACCURACY_FINE";
@@ -764,68 +794,307 @@
}
}
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
+ /**
+ * A builder class for {@link LocationRequest}.
+ */
+ public static final class Builder {
- LocationRequest that = (LocationRequest) o;
- return mQuality == that.mQuality
- && mInterval == that.mInterval
- && mFastestInterval == that.mFastestInterval
- && mExplicitFastestInterval == that.mExplicitFastestInterval
- && mExpireAt == that.mExpireAt
- && mExpireIn == that.mExpireIn
- && mNumUpdates == that.mNumUpdates
- && Float.compare(that.mSmallestDisplacement, mSmallestDisplacement) == 0
- && mHideFromAppOps == that.mHideFromAppOps
- && mLocationSettingsIgnored == that.mLocationSettingsIgnored
- && mLowPowerMode == that.mLowPowerMode
- && mProvider.equals(that.mProvider)
- && Objects.equals(mWorkSource, that.mWorkSource);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mProvider, mInterval, mWorkSource);
- }
-
- @NonNull
- @Override
- public String toString() {
- StringBuilder s = new StringBuilder();
- s.append("Request[");
- s.append(qualityToString(mQuality));
- s.append(" ").append(mProvider);
- if (mQuality != POWER_NONE) {
- s.append(" interval=");
- TimeUtils.formatDuration(mInterval, s);
- if (mExplicitFastestInterval && mFastestInterval != mInterval) {
- s.append(" fastestInterval=");
- TimeUtils.formatDuration(mFastestInterval, s);
+ private static int checkQuality(int quality, boolean allowDeprecated) {
+ switch (quality) {
+ case ACCURACY_FINE:
+ // fall through
+ case ACCURACY_BLOCK:
+ // fall through
+ case ACCURACY_CITY:
+ // fall through
+ case POWER_LOW:
+ // fall through
+ case POWER_HIGH:
+ return quality;
+ case POWER_NONE:
+ if (allowDeprecated) {
+ return quality;
+ }
+ // fall through
+ default:
+ throw new IllegalArgumentException("invalid quality: " + quality);
}
}
- if (mExpireAt != Long.MAX_VALUE) {
- s.append(" expireAt=").append(TimeUtils.formatRealtime(mExpireAt));
+
+ private long mIntervalMillis;
+ private int mQuality;
+ private long mDurationMillis;
+ private int mMaxUpdates;
+ private long mMinUpdateIntervalMillis;
+ private float mMinUpdateDistanceMeters;
+ private boolean mHiddenFromAppOps;
+ private boolean mLocationSettingsIgnored;
+ private boolean mLowPower;
+ @Nullable private WorkSource mWorkSource;
+
+ /**
+ * Creates a new Builder with the given interval. See {@link #setIntervalMillis(long)} for
+ * more information on the interval.
+ */
+ public Builder(long intervalMillis) {
+ // gives us a range check
+ setIntervalMillis(intervalMillis);
+
+ mQuality = ACCURACY_BLOCK;
+ mDurationMillis = Long.MAX_VALUE;
+ mMaxUpdates = Integer.MAX_VALUE;
+ mMinUpdateIntervalMillis = IMPLICIT_MIN_UPDATE_INTERVAL;
+ mMinUpdateDistanceMeters = 0;
+ mHiddenFromAppOps = false;
+ mLocationSettingsIgnored = false;
+ mLowPower = false;
+ mWorkSource = null;
}
- if (mExpireIn != Long.MAX_VALUE) {
- s.append(" expireIn=");
- TimeUtils.formatDuration(mExpireIn, s);
+
+ /**
+ * Creates a new Builder with all parameters copied from the given location request.
+ */
+ public Builder(@NonNull LocationRequest locationRequest) {
+ mIntervalMillis = locationRequest.mInterval;
+ mQuality = locationRequest.mQuality;
+ mDurationMillis = locationRequest.mDurationMillis;
+ mMaxUpdates = locationRequest.mMaxUpdates;
+ mMinUpdateIntervalMillis = locationRequest.mMinUpdateIntervalMillis;
+ mMinUpdateDistanceMeters = locationRequest.mMinUpdateDistanceMeters;
+ mHiddenFromAppOps = locationRequest.mHideFromAppOps;
+ mLocationSettingsIgnored = locationRequest.mLocationSettingsIgnored;
+ mLowPower = locationRequest.mLowPower;
+ mWorkSource = locationRequest.mWorkSource;
+
+ // handle edge cases that can only happen with location request that has been modified
+ // by deprecated SystemApi methods
+ if (mQuality == POWER_NONE) {
+ mIntervalMillis = PASSIVE_INTERVAL;
+ }
+ if (mIntervalMillis == PASSIVE_INTERVAL
+ && mMinUpdateIntervalMillis == IMPLICIT_MIN_UPDATE_INTERVAL) {
+ // this is the legacy default minimum update interval, so if we're forced to
+ // change the value, at least this should be unsuprising to legacy clients (which
+ // should be the only clients capable of getting in this weird state).
+ mMinUpdateIntervalMillis = 10 * 60 * 1000;
+ }
}
- if (mNumUpdates != Integer.MAX_VALUE) {
- s.append(" num=").append(mNumUpdates);
+
+ /**
+ * Sets the request interval. The request interval may be set to {@link #PASSIVE_INTERVAL}
+ * which indicates this request will not actively generate location updates (and thus will
+ * not be power blamed for location), but may receive location updates generated as a result
+ * of other location requests. A passive request must always have an explicit minimum
+ * update interval set.
+ *
+ * <p>Locations may be available at a faster interval than specified here, see
+ * {@link #setMinUpdateIntervalMillis(long)} for the behavior in that case.
+ */
+ public @NonNull Builder setIntervalMillis(@IntRange(from = 0) long intervalMillis) {
+ mIntervalMillis = Preconditions.checkArgumentInRange(intervalMillis, 0, Long.MAX_VALUE,
+ "intervalMillis");
+ return this;
}
- if (mLowPowerMode) {
- s.append(" lowPowerMode");
+
+ /**
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Builder setQuality(int quality) {
+ mQuality = checkQuality(quality, false);
+ return this;
}
- if (mLocationSettingsIgnored) {
- s.append(" locationSettingsIgnored");
+
+ /**
+ * @hide
+ */
+ public @NonNull Builder setQuality(@NonNull Criteria criteria) {
+ switch (criteria.getAccuracy()) {
+ case Criteria.ACCURACY_COARSE:
+ mQuality = ACCURACY_BLOCK;
+ break;
+ case Criteria.ACCURACY_FINE:
+ mQuality = ACCURACY_FINE;
+ break;
+ default: {
+ if (criteria.getPowerRequirement() == Criteria.POWER_HIGH) {
+ mQuality = POWER_HIGH;
+ } else {
+ mQuality = POWER_LOW;
+ }
+ }
+ }
+ return this;
}
- s.append(']');
- return s.toString();
+
+ /**
+ * Sets the duration this request will continue before being automatically removed. Defaults
+ * to <code>Long.MAX_VALUE</code>, which represents an unlimited duration.
+ */
+ public @NonNull Builder setDurationMillis(@IntRange(from = 1) long durationMillis) {
+ mDurationMillis = Preconditions.checkArgumentInRange(durationMillis, 1, Long.MAX_VALUE,
+ "durationMillis");
+ return this;
+ }
+
+ /**
+ * Sets the maximum number of location updates for this request before this request is
+ * automatically removed. Defaults to <code>Integer.MAX_VALUE</code>, which represents an
+ * unlimited number of updates.
+ */
+ public @NonNull Builder setMaxUpdates(
+ @IntRange(from = 1, to = Integer.MAX_VALUE) int maxUpdates) {
+ mMaxUpdates = Preconditions.checkArgumentInRange(maxUpdates, 1, Integer.MAX_VALUE,
+ "maxUpdates");
+ return this;
+ }
+
+ /**
+ * Sets an explicit minimum update interval. If location updates are available faster than
+ * the request interval then an update will only occur if the minimum update interval has
+ * expired since the last location update. Defaults to no explicit minimum update interval
+ * set, which means the minimum update interval is the same as the interval.
+ *
+ * <p class=note><strong>Note:</strong> Some allowance for jitter is already built into the
+ * minimum update interval, so you need not worry about updates blocked simply because they
+ * arrived a fraction of a second earlier than expected.
+ *
+ * <p class="note"><strong>Note:</strong> When {@link #build()} is invoked, the minimum of
+ * the interval and the minimum update interval will be used as the minimum update interval
+ * of the built request.
+ */
+ public @NonNull Builder setMinUpdateIntervalMillis(
+ @IntRange(from = 0) long minUpdateIntervalMillis) {
+ mMinUpdateIntervalMillis = Preconditions.checkArgumentInRange(minUpdateIntervalMillis,
+ 0, Long.MAX_VALUE, "minUpdateIntervalMillis");
+ return this;
+ }
+
+ /**
+ * Clears an explicitly set minimum update interval and reverts to an implicit minimum
+ * update interval (ie, the minimum update interval is the same value as the interval).
+ */
+ public @NonNull Builder clearMinUpdateIntervalMillis() {
+ mMinUpdateIntervalMillis = IMPLICIT_MIN_UPDATE_INTERVAL;
+ return this;
+ }
+
+ /**
+ * Sets the minimum update distance between location updates. If a potential location
+ * update is closer to the last location update than the minimum update distance, then
+ * the potential location update will not occur. Defaults to 0, which represents no minimum
+ * update distance.
+ */
+ public @NonNull Builder setMinUpdateDistanceMeters(
+ @FloatRange(from = 0, to = Float.MAX_VALUE) float minUpdateDistanceMeters) {
+ mMinUpdateDistanceMeters = Preconditions.checkArgumentInRange(minUpdateDistanceMeters,
+ 0, Float.MAX_VALUE, "minUpdateDistanceMeters");
+ return this;
+ }
+
+ /**
+ * If set to true, indicates that app ops should not be updated with location usage due to
+ * this request. This implies that someone else (usually the creator of the location
+ * request) is responsible for updating app ops as appropriate. Defaults to false.
+ *
+ * <p>Permissions enforcement occurs when resulting location request is actually used, not
+ * when this method is invoked.
+ *
+ * @hide
+ */
+ @TestApi
+ @SystemApi
+ @RequiresPermission(Manifest.permission.UPDATE_APP_OPS_STATS)
+ public @NonNull Builder setHiddenFromAppOps(boolean hiddenFromAppOps) {
+ mHiddenFromAppOps = hiddenFromAppOps;
+ return this;
+ }
+
+ /**
+ * If set to true, indicates that location settings, throttling, background location limits,
+ * and any other possible limiting factors should be ignored in order to satisfy this
+ * request. This is only intended for use in user initiated emergency situations, and
+ * should be used extremely cautiously. Defaults to false.
+ *
+ * <p>Permissions enforcement occurs when resulting location request is actually used, not
+ * when this method is invoked.
+ *
+ * @hide
+ */
+ @TestApi
+ @SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ public @NonNull Builder setLocationSettingsIgnored(boolean locationSettingsIgnored) {
+ mLocationSettingsIgnored = locationSettingsIgnored;
+ return this;
+ }
+
+ /**
+ * It set to true, indicates that extreme trade-offs should be made if possible to save
+ * power for this request. This usually involves specialized hardware modes which can
+ * greatly affect the quality of locations. Defaults to false.
+ *
+ * <p>Permissions enforcement occurs when resulting location request is actually used, not
+ * when this method is invoked.
+ *
+ * @hide
+ */
+ @TestApi
+ @SystemApi
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public @NonNull Builder setLowPower(boolean lowPower) {
+ mLowPower = lowPower;
+ return this;
+ }
+
+ /**
+ * Sets the work source to use for power blame for this location request. Defaults to null,
+ * which implies the system is free to assign power blame as it determines best for this
+ * request (which usually means blaming the owner of the location listener).
+ *
+ * <p>Permissions enforcement occurs when resulting location request is actually used, not
+ * when this method is invoked.
+ *
+ * @hide
+ */
+ @TestApi
+ @SystemApi
+ @RequiresPermission(Manifest.permission.UPDATE_DEVICE_STATS)
+ public @NonNull Builder setWorkSource(@Nullable WorkSource workSource) {
+ mWorkSource = workSource;
+ return this;
+ }
+
+ /**
+ * Builds a location request from this builder. If an explicit minimum update interval is
+ * set, the minimum update interval of the location request will be the minimum of the
+ * interval and minimum update interval.
+ *
+ * <p>If building a passive request then you must have set an explicit minimum update
+ * interval.
+ *
+ * @throws IllegalStateException if building a passive request with no explicit minimum
+ * update interval set
+ * @return a new location request
+ */
+ public @NonNull LocationRequest build() {
+ Preconditions.checkState(mIntervalMillis != PASSIVE_INTERVAL
+ || mMinUpdateIntervalMillis != IMPLICIT_MIN_UPDATE_INTERVAL,
+ "passive location requests must have an explicit minimum update interval");
+
+ return new LocationRequest(
+ null,
+ mIntervalMillis,
+ mIntervalMillis != PASSIVE_INTERVAL ? mQuality : POWER_NONE,
+ Long.MAX_VALUE,
+ mDurationMillis,
+ mMaxUpdates,
+ min(mMinUpdateIntervalMillis, mIntervalMillis),
+ mMinUpdateDistanceMeters,
+ mHiddenFromAppOps,
+ mLocationSettingsIgnored,
+ mLowPower,
+ mWorkSource);
+ }
}
}
diff --git a/location/java/com/android/internal/location/ILocationProvider.aidl b/location/java/com/android/internal/location/ILocationProvider.aidl
index 4246c6c..01570dc 100644
--- a/location/java/com/android/internal/location/ILocationProvider.aidl
+++ b/location/java/com/android/internal/location/ILocationProvider.aidl
@@ -29,12 +29,12 @@
*/
interface ILocationProvider {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 30, publicAlternatives = "{@code Do not use}")
oneway void setLocationProviderManager(in ILocationProviderManager manager);
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 30, publicAlternatives = "{@code Do not use}")
oneway void setRequest(in ProviderRequest request, in WorkSource ws);
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 30, publicAlternatives = "{@code Do not use}")
oneway void sendExtraCommand(String command, in Bundle extras);
}
diff --git a/location/java/com/android/internal/location/ProviderProperties.java b/location/java/com/android/internal/location/ProviderProperties.java
index c3439c5..dbb61d2 100644
--- a/location/java/com/android/internal/location/ProviderProperties.java
+++ b/location/java/com/android/internal/location/ProviderProperties.java
@@ -43,86 +43,96 @@
@IntDef({Criteria.ACCURACY_FINE, Criteria.ACCURACY_COARSE})
public @interface Accuracy {}
- /**
- * True if provider requires access to a
- * data network (e.g., the Internet), false otherwise.
- */
- public final boolean mRequiresNetwork;
+ private final boolean mHasNetworkRequirement;
+ private final boolean mHasSatelliteRequirement;
+ private final boolean mHasCellRequirement;
+ private final boolean mHasMonetaryCost;
+ private final boolean mHasAltitudeSupport;
+ private final boolean mHasSpeedSupport;
+ private final boolean mHasBearingSupport;
+ private final @PowerRequirement int mPowerRequirement;
+ private final @Accuracy int mAccuracy;
+
+ public ProviderProperties(boolean hasNetworkRequirement, boolean hasSatelliteRequirement,
+ boolean hasCellRequirement, boolean hasMonetaryCost, boolean hasAltitudeSupport,
+ boolean hasSpeedSupport, boolean hasBearingSupport,
+ @PowerRequirement int powerRequirement, @Accuracy int accuracy) {
+ mHasNetworkRequirement = hasNetworkRequirement;
+ mHasSatelliteRequirement = hasSatelliteRequirement;
+ mHasCellRequirement = hasCellRequirement;
+ mHasMonetaryCost = hasMonetaryCost;
+ mHasAltitudeSupport = hasAltitudeSupport;
+ mHasSpeedSupport = hasSpeedSupport;
+ mHasBearingSupport = hasBearingSupport;
+ mPowerRequirement = Preconditions.checkArgumentInRange(powerRequirement, Criteria.POWER_LOW,
+ Criteria.POWER_HIGH, "powerRequirement");
+ mAccuracy = Preconditions.checkArgumentInRange(accuracy, Criteria.ACCURACY_FINE,
+ Criteria.ACCURACY_COARSE, "accuracy");
+ }
/**
- * True if the provider requires access to a
- * satellite-based positioning system (e.g., GPS), false
- * otherwise.
+ * True if provider requires access to a data network (e.g., the Internet).
*/
- public final boolean mRequiresSatellite;
+ public boolean hasNetworkRequirement() {
+ return mHasNetworkRequirement;
+ }
/**
- * True if the provider requires access to an appropriate
- * cellular network (e.g., to make use of cell tower IDs), false
- * otherwise.
+ * True if the provider requires access to a satellite-based positioning system (e.g., GPS).
*/
- public final boolean mRequiresCell;
+ public boolean hasSatelliteRequirement() {
+ return mHasSatelliteRequirement;
+ }
/**
- * True if the use of this provider may result in a
- * monetary charge to the user, false if use is free. It is up to
- * each provider to give accurate information. Cell (network) usage
- * is not considered monetary cost.
+ * True if the provider requires access to a cellular network (e.g., for cell tower IDs).
*/
- public final boolean mHasMonetaryCost;
+ public boolean hasCellRequirement() {
+ return mHasCellRequirement;
+ }
/**
- * True if the provider is able to provide altitude
- * information, false otherwise. A provider that reports altitude
- * under most circumstances but may occasionally not report it
- * should return true.
+ * True if this provider may result in a monetary charge to the user. Network usage is not
+ * considered a monetary cost.
*/
- public final boolean mSupportsAltitude;
+ public boolean hasMonetaryCost() {
+ return mHasMonetaryCost;
+ }
/**
- * True if the provider is able to provide speed
- * information, false otherwise. A provider that reports speed
- * under most circumstances but may occasionally not report it
- * should return true.
+ * True if the provider is able to provide altitude under at least some conditions.
*/
- public final boolean mSupportsSpeed;
+ public boolean hasAltitudeSupport() {
+ return mHasAltitudeSupport;
+ }
/**
- * True if the provider is able to provide bearing
- * information, false otherwise. A provider that reports bearing
- * under most circumstances but may occasionally not report it
- * should return true.
+ * True if the provider is able to provide speed under at least some conditions.
*/
- public final boolean mSupportsBearing;
+ public boolean hasSpeedSupport() {
+ return mHasSpeedSupport;
+ }
+
+ /**
+ * True if the provider is able to provide bearing under at least some conditions.
+ */
+ public boolean hasBearingSupport() {
+ return mHasBearingSupport;
+ }
/**
* Power requirement for this provider.
*/
- @PowerRequirement
- public final int mPowerRequirement;
+ public @PowerRequirement int getPowerRequirement() {
+ return mPowerRequirement;
+ }
/**
* Constant describing the horizontal accuracy returned
* by this provider.
*/
- @Accuracy
- public final int mAccuracy;
-
- public ProviderProperties(boolean requiresNetwork, boolean requiresSatellite,
- boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
- boolean supportsSpeed, boolean supportsBearing, @PowerRequirement int powerRequirement,
- @Accuracy int accuracy) {
- mRequiresNetwork = requiresNetwork;
- mRequiresSatellite = requiresSatellite;
- mRequiresCell = requiresCell;
- mHasMonetaryCost = hasMonetaryCost;
- mSupportsAltitude = supportsAltitude;
- mSupportsSpeed = supportsSpeed;
- mSupportsBearing = supportsBearing;
- mPowerRequirement = Preconditions.checkArgumentInRange(powerRequirement, Criteria.POWER_LOW,
- Criteria.POWER_HIGH, "powerRequirement");
- mAccuracy = Preconditions.checkArgumentInRange(accuracy, Criteria.ACCURACY_FINE,
- Criteria.ACCURACY_COARSE, "accuracy");
+ public @Accuracy int getAccuracy() {
+ return mAccuracy;
}
public static final Parcelable.Creator<ProviderProperties> CREATOR =
@@ -130,13 +140,13 @@
@Override
public ProviderProperties createFromParcel(Parcel in) {
return new ProviderProperties(
- /* requiresNetwork= */ in.readBoolean(),
- /* requiresSatellite= */ in.readBoolean(),
- /* requiresCell= */ in.readBoolean(),
+ /* hasNetworkRequirement= */ in.readBoolean(),
+ /* hasSatelliteRequirement= */ in.readBoolean(),
+ /* hasCellRequirement= */ in.readBoolean(),
/* hasMonetaryCost= */ in.readBoolean(),
- /* supportsAltitude= */ in.readBoolean(),
- /* supportsSpeed= */ in.readBoolean(),
- /* supportsBearing= */ in.readBoolean(),
+ /* hasAltitudeSupport= */ in.readBoolean(),
+ /* hasSpeedSupport= */ in.readBoolean(),
+ /* hasBearingSupport= */ in.readBoolean(),
/* powerRequirement= */ in.readInt(),
/* accuracy= */ in.readInt());
}
@@ -154,13 +164,13 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeBoolean(mRequiresNetwork);
- parcel.writeBoolean(mRequiresSatellite);
- parcel.writeBoolean(mRequiresCell);
+ parcel.writeBoolean(mHasNetworkRequirement);
+ parcel.writeBoolean(mHasSatelliteRequirement);
+ parcel.writeBoolean(mHasCellRequirement);
parcel.writeBoolean(mHasMonetaryCost);
- parcel.writeBoolean(mSupportsAltitude);
- parcel.writeBoolean(mSupportsSpeed);
- parcel.writeBoolean(mSupportsBearing);
+ parcel.writeBoolean(mHasAltitudeSupport);
+ parcel.writeBoolean(mHasSpeedSupport);
+ parcel.writeBoolean(mHasBearingSupport);
parcel.writeInt(mPowerRequirement);
parcel.writeInt(mAccuracy);
}
@@ -174,21 +184,22 @@
return false;
}
ProviderProperties that = (ProviderProperties) o;
- return mRequiresNetwork == that.mRequiresNetwork
- && mRequiresSatellite == that.mRequiresSatellite
- && mRequiresCell == that.mRequiresCell
+ return mHasNetworkRequirement == that.mHasNetworkRequirement
+ && mHasSatelliteRequirement == that.mHasSatelliteRequirement
+ && mHasCellRequirement == that.mHasCellRequirement
&& mHasMonetaryCost == that.mHasMonetaryCost
- && mSupportsAltitude == that.mSupportsAltitude
- && mSupportsSpeed == that.mSupportsSpeed
- && mSupportsBearing == that.mSupportsBearing
+ && mHasAltitudeSupport == that.mHasAltitudeSupport
+ && mHasSpeedSupport == that.mHasSpeedSupport
+ && mHasBearingSupport == that.mHasBearingSupport
&& mPowerRequirement == that.mPowerRequirement
&& mAccuracy == that.mAccuracy;
}
@Override
public int hashCode() {
- return Objects.hash(mRequiresNetwork, mRequiresSatellite, mRequiresCell, mHasMonetaryCost,
- mSupportsAltitude, mSupportsSpeed, mSupportsBearing, mPowerRequirement, mAccuracy);
+ return Objects.hash(mHasNetworkRequirement, mHasSatelliteRequirement, mHasCellRequirement,
+ mHasMonetaryCost, mHasAltitudeSupport, mHasSpeedSupport, mHasBearingSupport,
+ mPowerRequirement, mAccuracy);
}
@Override
@@ -196,15 +207,15 @@
StringBuilder b = new StringBuilder("ProviderProperties[");
b.append("power=").append(powerToString(mPowerRequirement)).append(", ");
b.append("accuracy=").append(accuracyToString(mAccuracy));
- if (mRequiresNetwork || mRequiresSatellite || mRequiresCell) {
+ if (mHasNetworkRequirement || mHasSatelliteRequirement || mHasCellRequirement) {
b.append(", requires=");
- if (mRequiresNetwork) {
+ if (mHasNetworkRequirement) {
b.append("network,");
}
- if (mRequiresSatellite) {
+ if (mHasSatelliteRequirement) {
b.append("satellite,");
}
- if (mRequiresCell) {
+ if (mHasCellRequirement) {
b.append("cell,");
}
b.setLength(b.length() - 1);
@@ -212,15 +223,15 @@
if (mHasMonetaryCost) {
b.append(", hasMonetaryCost");
}
- if (mSupportsBearing || mSupportsSpeed || mSupportsAltitude) {
+ if (mHasBearingSupport || mHasSpeedSupport || mHasAltitudeSupport) {
b.append(", supports=[");
- if (mSupportsBearing) {
+ if (mHasBearingSupport) {
b.append("bearing, ");
}
- if (mSupportsSpeed) {
+ if (mHasSpeedSupport) {
b.append("speed, ");
}
- if (mSupportsAltitude) {
+ if (mHasAltitudeSupport) {
b.append("altitude, ");
}
b.setLength(b.length() - 2);
diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/com/android/internal/location/ProviderRequest.java
index 5d072f8..fee86ce 100644
--- a/location/java/com/android/internal/location/ProviderRequest.java
+++ b/location/java/com/android/internal/location/ProviderRequest.java
@@ -16,66 +16,101 @@
package com.android.internal.location;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.location.LocationRequest;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.WorkSource;
import android.util.TimeUtils;
+import com.android.internal.util.Preconditions;
+
import java.util.Collections;
import java.util.List;
import java.util.Objects;
-/** @hide */
+/**
+ * Location provider request.
+ * @hide
+ */
public final class ProviderRequest implements Parcelable {
- public static final ProviderRequest EMPTY_REQUEST = new ProviderRequest(false, Long.MAX_VALUE,
- false, false,
- Collections.emptyList(), new WorkSource());
+ public static final long INTERVAL_DISABLED = Long.MAX_VALUE;
- /** Location reporting is requested (true) */
- @UnsupportedAppUsage
+ public static final ProviderRequest EMPTY_REQUEST = new ProviderRequest(
+ INTERVAL_DISABLED, false, false, Collections.emptyList(), new WorkSource());
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
+ + "ProviderRequest}")
public final boolean reportLocation;
-
- /** The smallest requested interval */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
+ + "ProviderRequest}")
public final long interval;
-
- /**
- * Whether provider shall make stronger than normal tradeoffs to substantially restrict power
- * use.
- */
- public final boolean lowPowerMode;
-
- /**
- * When this flag is true, providers should ignore all location settings, user consents, power
- * restrictions or any other restricting factors and always satisfy this request to the best of
- * their ability. This flag should only be used in event of an emergency.
- */
- public final boolean locationSettingsIgnored;
-
- /**
- * A more detailed set of requests.
- * <p>Location Providers can optionally use this to
- * fine tune location updates, for example when there
- * is a high power slow interval request and a
- * low power fast interval request.
- */
- @UnsupportedAppUsage
+ private final boolean mLowPower;
+ private final boolean mLocationSettingsIgnored;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
+ + "ProviderRequest}")
public final List<LocationRequest> locationRequests;
+ private final WorkSource mWorkSource;
- public final WorkSource workSource;
+ private ProviderRequest(long intervalMillis, boolean lowPower,
+ boolean locationSettingsIgnored, @NonNull List<LocationRequest> locationRequests,
+ @NonNull WorkSource workSource) {
+ reportLocation = intervalMillis != INTERVAL_DISABLED;
+ interval = intervalMillis;
+ mLowPower = lowPower;
+ mLocationSettingsIgnored = locationSettingsIgnored;
+ this.locationRequests = locationRequests;
+ mWorkSource = workSource;
+ }
- private ProviderRequest(boolean reportLocation, long interval, boolean lowPowerMode,
- boolean locationSettingsIgnored, List<LocationRequest> locationRequests,
- WorkSource workSource) {
- this.reportLocation = reportLocation;
- this.interval = interval;
- this.lowPowerMode = lowPowerMode;
- this.locationSettingsIgnored = locationSettingsIgnored;
- this.locationRequests = Objects.requireNonNull(locationRequests);
- this.workSource = Objects.requireNonNull(workSource);
+ /**
+ * True if this is an active request with a valid location reporting interval, false if this
+ * request is inactive and does not require any locations to be reported.
+ */
+ public boolean isActive() {
+ return interval != INTERVAL_DISABLED;
+ }
+
+ /**
+ * The interval at which a provider should report location. Will return
+ * {@link #INTERVAL_DISABLED} for an inactive request.
+ */
+ public @IntRange(from = 0) long getIntervalMillis() {
+ return interval;
+ }
+
+ /**
+ * Whether any applicable hardware low power modes should be used to satisfy this request.
+ */
+ public boolean isLowPower() {
+ return mLowPower;
+ }
+
+ /**
+ * Whether the provider should ignore all location settings, user consents, power restrictions
+ * or any other restricting factors and always satisfy this request to the best of their
+ * ability. This should only be used in case of a user initiated emergency.
+ */
+ public boolean isLocationSettingsIgnored() {
+ return mLocationSettingsIgnored;
+ }
+
+ /**
+ * The full list of location requests contributing to this provider request.
+ */
+ public @NonNull List<LocationRequest> getLocationRequests() {
+ return locationRequests;
+ }
+
+ /**
+ * The power blame for this provider request.
+ */
+ public @NonNull WorkSource getWorkSource() {
+ return mWorkSource;
}
public static final Parcelable.Creator<ProviderRequest> CREATOR =
@@ -83,9 +118,8 @@
@Override
public ProviderRequest createFromParcel(Parcel in) {
return new ProviderRequest(
- /* reportLocation= */ in.readBoolean(),
- /* interval= */ in.readLong(),
- /* lowPowerMode= */ in.readBoolean(),
+ /* intervalMillis= */ in.readLong(),
+ /* lowPower= */ in.readBoolean(),
/* locationSettingsIgnored= */ in.readBoolean(),
/* locationRequests= */
in.createTypedArrayList(LocationRequest.CREATOR),
@@ -105,29 +139,54 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeBoolean(reportLocation);
parcel.writeLong(interval);
- parcel.writeBoolean(lowPowerMode);
- parcel.writeBoolean(locationSettingsIgnored);
+ parcel.writeBoolean(mLowPower);
+ parcel.writeBoolean(mLocationSettingsIgnored);
parcel.writeTypedList(locationRequests);
- parcel.writeTypedObject(workSource, flags);
+ parcel.writeTypedObject(mWorkSource, flags);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ProviderRequest that = (ProviderRequest) o;
+ if (interval == INTERVAL_DISABLED) {
+ return that.interval == INTERVAL_DISABLED;
+ } else {
+ return interval == that.interval
+ && mLowPower == that.mLowPower
+ && mLocationSettingsIgnored == that.mLocationSettingsIgnored
+ && locationRequests.equals(that.locationRequests)
+ && mWorkSource.equals(that.mWorkSource);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(interval, mWorkSource);
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append("ProviderRequest[");
- if (reportLocation) {
- s.append("interval=");
+ if (interval != INTERVAL_DISABLED) {
+ s.append("@");
TimeUtils.formatDuration(interval, s);
- if (lowPowerMode) {
- s.append(", lowPowerMode");
+ if (mLowPower) {
+ s.append(", lowPower");
}
- if (locationSettingsIgnored) {
+ if (mLocationSettingsIgnored) {
s.append(", locationSettingsIgnored");
}
- if (!workSource.isEmpty()) {
- s.append(", ").append(workSource);
+ if (!mWorkSource.isEmpty()) {
+ s.append(", ").append(mWorkSource);
}
} else {
s.append("OFF");
@@ -140,71 +199,64 @@
* A Builder for {@link ProviderRequest}s.
*/
public static class Builder {
- private long mInterval = Long.MAX_VALUE;
- private boolean mLowPowerMode;
+ private long mIntervalMillis = INTERVAL_DISABLED;
+ private boolean mLowPower;
private boolean mLocationSettingsIgnored;
private List<LocationRequest> mLocationRequests = Collections.emptyList();
private WorkSource mWorkSource = new WorkSource();
- public long getInterval() {
- return mInterval;
- }
-
- /** Sets the request interval. */
- public Builder setInterval(long interval) {
- this.mInterval = interval;
+ /**
+ * Sets the request interval. Use {@link #INTERVAL_DISABLED} for an inactive request.
+ * Defaults to {@link #INTERVAL_DISABLED}.
+ */
+ public @NonNull Builder setIntervalMillis(@IntRange(from = 0) long intervalMillis) {
+ mIntervalMillis = Preconditions.checkArgumentInRange(intervalMillis, 0, Long.MAX_VALUE,
+ "intervalMillis");
return this;
}
- public boolean isLowPowerMode() {
- return mLowPowerMode;
- }
-
- /** Sets whether low power mode is enabled. */
- public Builder setLowPowerMode(boolean lowPowerMode) {
- this.mLowPowerMode = lowPowerMode;
+ /**
+ * Sets whether hardware low power mode should be used. False by default.
+ */
+ public @NonNull Builder setLowPower(boolean lowPower) {
+ mLowPower = lowPower;
return this;
}
- public boolean isLocationSettingsIgnored() {
- return mLocationSettingsIgnored;
- }
-
- /** Sets whether location settings should be ignored. */
- public Builder setLocationSettingsIgnored(boolean locationSettingsIgnored) {
+ /**
+ * Sets whether location settings should be ignored. False by default.
+ */
+ public @NonNull Builder setLocationSettingsIgnored(boolean locationSettingsIgnored) {
this.mLocationSettingsIgnored = locationSettingsIgnored;
return this;
}
- public List<LocationRequest> getLocationRequests() {
- return mLocationRequests;
- }
-
- /** Sets the {@link LocationRequest}s associated with this request. */
- public Builder setLocationRequests(List<LocationRequest> locationRequests) {
+ /**
+ * Sets the {@link LocationRequest}s associated with this request. Empty by default.
+ */
+ public @NonNull Builder setLocationRequests(
+ @NonNull List<LocationRequest> locationRequests) {
this.mLocationRequests = Objects.requireNonNull(locationRequests);
return this;
}
- public WorkSource getWorkSource() {
- return mWorkSource;
- }
-
- /** Sets the work source. */
- public Builder setWorkSource(WorkSource workSource) {
+ /**
+ * Sets the work source for power blame. Empty by default.
+ */
+ public @NonNull Builder setWorkSource(@NonNull WorkSource workSource) {
mWorkSource = Objects.requireNonNull(workSource);
return this;
}
/**
- * Builds a ProviderRequest object with the set information.
+ * Builds a ProviderRequest.
*/
- public ProviderRequest build() {
- if (mInterval == Long.MAX_VALUE) {
+ public @NonNull ProviderRequest build() {
+ if (mIntervalMillis == INTERVAL_DISABLED) {
return EMPTY_REQUEST;
} else {
- return new ProviderRequest(true, mInterval, mLowPowerMode,
- mLocationSettingsIgnored, mLocationRequests, mWorkSource);
+ return new ProviderRequest(mIntervalMillis, mLowPower, mLocationSettingsIgnored,
+ mLocationRequests, mWorkSource);
}
}
}
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index 4d0acea..c00ff57 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -44,14 +44,17 @@
}
public final class ProviderPropertiesUnbundled {
- method public static com.android.location.provider.ProviderPropertiesUnbundled create(boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
+ method @NonNull public static com.android.location.provider.ProviderPropertiesUnbundled create(boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
}
public final class ProviderRequestUnbundled {
method public long getInterval();
- method public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests();
+ method @NonNull public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests();
method public boolean getReportLocation();
+ method @NonNull @RequiresApi(android.os.Build.VERSION_CODES.S) public android.os.WorkSource getWorkSource();
method @RequiresApi(android.os.Build.VERSION_CODES.Q) public boolean isLocationSettingsIgnored();
+ method @RequiresApi(android.os.Build.VERSION_CODES.S) public boolean isLowPower();
+ field public static final long INTERVAL_DISABLED = 9223372036854775807L; // 0x7fffffffffffffffL
}
}
diff --git a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java
index 2511c39..92e05ef 100644
--- a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java
@@ -82,25 +82,21 @@
}
/**
- * Get the desired interval of this request, in milliseconds.
+ * Get the location update interval.
*
- * @return desired interval in milliseconds, inexact
+ * @return location update interval
*/
public long getInterval() {
- return delegate.getInterval();
+ return delegate.getIntervalMillis();
}
/**
- * Get the fastest interval of this request, in milliseconds.
+ * Get the minimum delivery interval.
*
- * <p>The system will never provide location updates faster
- * than the minimum of {@link #getFastestInterval} and
- * {@link #getInterval}.
- *
- * @return fastest interval in milliseconds, exact
+ * @return minimum delivery interval
*/
public long getFastestInterval() {
- return delegate.getFastestInterval();
+ return delegate.getMinUpdateIntervalMillis();
}
/**
@@ -118,7 +114,7 @@
* @return minimum distance between location updates in meters
*/
public float getSmallestDisplacement() {
- return delegate.getSmallestDisplacement();
+ return delegate.getMinUpdateDistanceMeters();
}
/**
diff --git a/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java b/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java
index b1a1bda..21ee5f4 100644
--- a/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java
@@ -16,19 +16,24 @@
package com.android.location.provider;
+import android.annotation.NonNull;
+
import com.android.internal.location.ProviderProperties;
+import java.util.Objects;
+
/**
- * This class is an interface to Provider Properties for unbundled applications.
+ * Represents provider properties for unbundled applications.
*
- * <p>IMPORTANT: This class is effectively a public API for unbundled
- * applications, and must remain API stable. See README.txt in the root
- * of this package for more information.
+ * <p>IMPORTANT: This class is effectively a public API for unbundled applications, and must remain
+ * API stable.
*/
public final class ProviderPropertiesUnbundled {
- private final ProviderProperties mProperties;
- public static ProviderPropertiesUnbundled create(boolean requiresNetwork,
+ /**
+ * Create new instance of {@link ProviderPropertiesUnbundled} with the given arguments.
+ */
+ public static @NonNull ProviderPropertiesUnbundled create(boolean requiresNetwork,
boolean requiresSatellite, boolean requiresCell, boolean hasMonetaryCost,
boolean supportsAltitude, boolean supportsSpeed, boolean supportsBearing,
int powerRequirement, int accuracy) {
@@ -37,12 +42,14 @@
supportsBearing, powerRequirement, accuracy));
}
+ private final ProviderProperties mProperties;
+
private ProviderPropertiesUnbundled(ProviderProperties properties) {
- mProperties = properties;
+ mProperties = Objects.requireNonNull(properties);
}
/** @hide */
- public ProviderProperties getProviderProperties() {
+ public @NonNull ProviderProperties getProviderProperties() {
return mProperties;
}
@@ -50,4 +57,22 @@
public String toString() {
return mProperties.toString();
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ProviderPropertiesUnbundled that = (ProviderPropertiesUnbundled) o;
+ return mProperties.equals(that.mProperties);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mProperties);
+ }
}
diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
index b650efc..6f5fcc7 100644
--- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
@@ -16,8 +16,10 @@
package com.android.location.provider;
+import android.annotation.NonNull;
import android.location.LocationRequest;
import android.os.Build;
+import android.os.WorkSource;
import androidx.annotation.RequiresApi;
@@ -27,14 +29,18 @@
import java.util.List;
/**
- * This class is an interface to Provider Requests for unbundled applications.
+ * Represents a provider request for unbundled applications.
*
- * <p>IMPORTANT: This class is effectively a public API for unbundled
- * applications, and must remain API stable. See README.txt in the root
- * of this package for more information.
+ * <p>IMPORTANT: This class is effectively a public API for unbundled applications, and must remain
+ * API stable.
*/
public final class ProviderRequestUnbundled {
+ /**
+ * Represents a disabled request.
+ */
+ public static final long INTERVAL_DISABLED = ProviderRequest.INTERVAL_DISABLED;
+
private final ProviderRequest mRequest;
/** @hide */
@@ -42,31 +48,60 @@
mRequest = request;
}
+ /**
+ * True if this is an active request with a valid location reporting interval, false if this
+ * request is inactive and does not require any locations to be reported.
+ */
public boolean getReportLocation() {
- return mRequest.reportLocation;
- }
-
- public long getInterval() {
- return mRequest.interval;
- }
-
- @RequiresApi(Build.VERSION_CODES.Q)
- public boolean isLocationSettingsIgnored() {
- return mRequest.locationSettingsIgnored;
+ return mRequest.isActive();
}
/**
- * Never null.
+ * The interval at which a provider should report location. Will return
+ * {@link #INTERVAL_DISABLED} for an inactive request.
*/
- public List<LocationRequestUnbundled> getLocationRequests() {
+ public long getInterval() {
+ return mRequest.getIntervalMillis();
+ }
+
+ /**
+ * Whether any applicable hardware low power modes should be used to satisfy this request.
+ */
+ @RequiresApi(Build.VERSION_CODES.S)
+ public boolean isLowPower() {
+ return mRequest.isLowPower();
+ }
+
+ /**
+ * Whether the provider should ignore all location settings, user consents, power restrictions
+ * or any other restricting factors and always satisfy this request to the best of their
+ * ability. This should only be used in case of a user initiated emergency.
+ */
+ @RequiresApi(Build.VERSION_CODES.Q)
+ public boolean isLocationSettingsIgnored() {
+ return mRequest.isLocationSettingsIgnored();
+ }
+
+ /**
+ * The full list of location requests contributing to this provider request.
+ */
+ public @NonNull List<LocationRequestUnbundled> getLocationRequests() {
List<LocationRequestUnbundled> result = new ArrayList<>(
- mRequest.locationRequests.size());
- for (LocationRequest r : mRequest.locationRequests) {
- result.add(new LocationRequestUnbundled(r));
+ mRequest.getLocationRequests().size());
+ for (LocationRequest r : mRequest.getLocationRequests()) {
+ result.add(new LocationRequestUnbundled(r));
}
return result;
}
+ /**
+ * The power blame for this provider request.
+ */
+ @RequiresApi(Build.VERSION_CODES.S)
+ public @NonNull WorkSource getWorkSource() {
+ return mRequest.getWorkSource();
+ }
+
@Override
public String toString() {
return mRequest.toString();
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
index 6dc0c6f..cf2f0f0 100644
--- a/media/TEST_MAPPING
+++ b/media/TEST_MAPPING
@@ -4,21 +4,13 @@
"name": "GtsMediaTestCases",
"options" : [
{
- "include-annotation": "android.platform.test.annotations.Presubmit"
+ "include-annotation": "android.platform.test.annotations.Presubmit"
},
{
"include-filter": "com.google.android.media.gts.WidevineGenericOpsTests"
- }
- ]
- },
- {
- "name": "GtsExoPlayerTestCases",
- "options" : [
- {
- "include-annotation": "android.platform.test.annotations.SocPresubmit"
},
{
- "include-filter": "com.google.android.exoplayer.gts.DashTest#testWidevine23FpsH264Fixed"
+ "include-filter": "com.google.android.media.gts.WidevineYouTubePerformanceTests"
}
]
}
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index f898931..45f1ca0 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -19,6 +19,8 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
import android.os.Build;
/**
@@ -418,7 +420,11 @@
* resolution and higher audio sampling rate, etc, than those with lower quality
* level.
*
- * @param cameraId the id for the camera
+ * @param cameraId the id for the camera. Integer camera ids parsed from the list received by
+ * invoking {@link CameraManager#getCameraIdList} can be used as long as they
+ * are {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE}
+ * and not
+ * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL EXTERNAL}.
* @param quality the target quality level for the camcorder profile.
* @see #QUALITY_LOW
* @see #QUALITY_HIGH
@@ -512,7 +518,11 @@
* @see android.hardware.camera2.CameraManager
* @see android.hardware.camera2.CameraCharacteristics
*
- * @param cameraId the id for the camera
+ * @param cameraId the id for the camera. Integer camera ids parsed from the list received by
+ * invoking {@link CameraManager#getCameraIdList} can be used as long as they
+ * are {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE}
+ * and not
+ * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL EXTERNAL}.
* @param quality the target quality level for the camcorder profile
*/
public static boolean hasProfile(int cameraId, int quality) {
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index d8a0bb3..d248f61 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -63,6 +63,7 @@
case ImageFormat.DEPTH16:
case ImageFormat.DEPTH_POINT_CLOUD:
case ImageFormat.RAW_DEPTH:
+ case ImageFormat.RAW_DEPTH10:
case ImageFormat.DEPTH_JPEG:
case ImageFormat.HEIC:
return 1;
@@ -110,6 +111,10 @@
throw new IllegalArgumentException(
"Copy of RAW_DEPTH format has not been implemented");
}
+ if (src.getFormat() == ImageFormat.RAW_DEPTH10) {
+ throw new IllegalArgumentException(
+ "Copy of RAW_DEPTH10 format has not been implemented");
+ }
if (!(dst.getOwner() instanceof ImageWriter)) {
throw new IllegalArgumentException("Destination image is not from ImageWriter. Only"
+ " the images from ImageWriter are writable");
@@ -202,6 +207,7 @@
estimatedBytePerPixel = 1.0;
break;
case ImageFormat.RAW10:
+ case ImageFormat.RAW_DEPTH10:
estimatedBytePerPixel = 1.25;
break;
case ImageFormat.YV12:
@@ -264,6 +270,7 @@
case ImageFormat.RAW10:
case ImageFormat.RAW12:
case ImageFormat.RAW_DEPTH:
+ case ImageFormat.RAW_DEPTH10:
case ImageFormat.HEIC:
return new Size(image.getWidth(), image.getHeight());
case ImageFormat.PRIVATE:
diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java
index 451677f..e092411 100644
--- a/media/java/android/media/MediaTranscodeManager.java
+++ b/media/java/android/media/MediaTranscodeManager.java
@@ -274,7 +274,7 @@
private static IMediaTranscodingService getService(boolean retry) {
int retryCount = !retry ? 1 : CONNECT_SERVICE_RETRY_COUNT;
- Log.i(TAG, "get service with rety " + retryCount);
+ Log.i(TAG, "get service with retry " + retryCount);
for (int count = 1; count <= retryCount; count++) {
Log.d(TAG, "Trying to connect to service. Try count: " + count);
IMediaTranscodingService service = IMediaTranscodingService.Stub.asInterface(
@@ -356,7 +356,15 @@
for (TranscodingJob job : retryJobs) {
// Notify the job failure if we fails to connect to the service or fail
// to retry the job.
- if (!haveTranscodingClient || !job.retry()) {
+ if (!haveTranscodingClient) {
+ // TODO(hkuang): Return correct error code to the client.
+ handleTranscodingFailed(job.getJobId(), 0 /*unused */);
+ }
+
+ try {
+ // Do not set hasRetried for retry initiated by MediaTranscodeManager.
+ job.retryInternal(false /*setHasRetried*/);
+ } catch (Exception re) {
// TODO(hkuang): Return correct error code to the client.
handleTranscodingFailed(job.getJobId(), 0 /*unused */);
}
@@ -1010,9 +1018,9 @@
@IntRange(from = 0, to = 100) int progress);
}
- private final ITranscodingClient mJobOwner;
- private final Executor mListenerExecutor;
- private final OnTranscodingFinishedListener mListener;
+ private final MediaTranscodeManager mManager;
+ private Executor mListenerExecutor;
+ private OnTranscodingFinishedListener mListener;
private int mJobId = -1;
// Lock for internal state.
private final Object mLock = new Object();
@@ -1028,20 +1036,26 @@
private @Status int mStatus = STATUS_PENDING;
@GuardedBy("mLock")
private @Result int mResult = RESULT_NONE;
+ @GuardedBy("mLock")
+ private boolean mHasRetried = false;
+ // The original request that associated with this job.
+ private final TranscodingRequest mRequest;
private TranscodingJob(
- @NonNull ITranscodingClient jobOwner,
+ @NonNull MediaTranscodeManager manager,
+ @NonNull TranscodingRequest request,
@NonNull TranscodingJobParcel parcel,
@NonNull @CallbackExecutor Executor executor,
@NonNull OnTranscodingFinishedListener listener) {
- Objects.requireNonNull(jobOwner, "JobOwner must not be null");
- Objects.requireNonNull(parcel, "TranscodingJobParcel must not be null");
+ Objects.requireNonNull(manager, "manager must not be null");
+ Objects.requireNonNull(parcel, "parcel must not be null");
Objects.requireNonNull(executor, "listenerExecutor must not be null");
Objects.requireNonNull(listener, "listener must not be null");
- mJobOwner = jobOwner;
+ mManager = manager;
mJobId = parcel.jobId;
mListenerExecutor = executor;
mListener = listener;
+ mRequest = request;
}
/**
@@ -1085,14 +1099,61 @@
/**
* 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.
*
- * @return true if successfully resubmit the job to the service. False otherwise.
+ * @throws MediaTranscodingException.ServiceNotAvailableException if the service
+ * is temporarily unavailable due to internal service rebooting. Client could retry
+ * again after receiving this exception.
+ * @throws UnsupportedOperationException if the retry could not be fulfilled.
+ * @hide
*/
- public boolean retry() {
+ public void retry() throws MediaTranscodingException.ServiceNotAvailableException {
+ retryInternal(true /*setHasRetried*/);
+ }
+
+ // TODO(hkuang): Add more test for it.
+ private void retryInternal(boolean setHasRetried)
+ throws MediaTranscodingException.ServiceNotAvailableException {
synchronized (mLock) {
- // TODO(hkuang): Implement this.
+ if (mStatus == STATUS_PENDING || mStatus == STATUS_RUNNING) {
+ throw new UnsupportedOperationException(
+ "Failed to retry as job is in processing");
+ }
+
+ if (mHasRetried) {
+ throw new UnsupportedOperationException("Job has been retried already");
+ }
+
+ // Get the client interface.
+ ITranscodingClient client = mManager.getTranscodingClient();
+ if (client == null) {
+ throw new MediaTranscodingException.ServiceNotAvailableException(
+ "Service rebooting. Try again later");
+ }
+
+ synchronized (mManager.mPendingTranscodingJobs) {
+ try {
+ // Submits the request to MediaTranscoding service.
+ TranscodingJobParcel jobParcel = new TranscodingJobParcel();
+ if (!client.submitRequest(mRequest.writeToParcel(), jobParcel)) {
+ mHasRetried = true;
+ throw new UnsupportedOperationException("Failed to enqueue request");
+ }
+
+ // Replace the old job id wit the new one.
+ mJobId = jobParcel.jobId;
+ // Adds the new job back into pending jobs.
+ mManager.mPendingTranscodingJobs.put(mJobId, this);
+ } catch (RemoteException re) {
+ throw new MediaTranscodingException.ServiceNotAvailableException(
+ "Failed to resubmit request to Transcoding service");
+ }
+ mStatus = STATUS_PENDING;
+ mHasRetried = setHasRetried ? true : false;
+ }
}
- return true;
}
/**
@@ -1105,7 +1166,11 @@
// Check if the job is finished already.
if (mStatus != STATUS_FINISHED) {
try {
- mJobOwner.cancelJob(mJobId);
+ ITranscodingClient client = mManager.getTranscodingClient();
+ // The client may be gone.
+ if (client != null) {
+ client.cancelJob(mJobId);
+ }
} 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);
@@ -1173,6 +1238,12 @@
}
}
+ private ITranscodingClient getTranscodingClient() {
+ synchronized (mLock) {
+ return mTranscodingClient;
+ }
+ }
+
/**
* Enqueues a TranscodingRequest for execution.
* <p> Upon successfully accepting the request, MediaTranscodeManager will return a
@@ -1185,13 +1256,17 @@
* @return A TranscodingJob 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
+ * is temporarily unavailable due to internal service rebooting. Client could retry
+ * again after receiving this exception.
*/
@NonNull
public TranscodingJob enqueueRequest(
@NonNull TranscodingRequest transcodingRequest,
@NonNull @CallbackExecutor Executor listenerExecutor,
@NonNull OnTranscodingFinishedListener listener)
- throws FileNotFoundException {
+ throws FileNotFoundException,
+ MediaTranscodingException.ServiceNotAvailableException {
Log.i(TAG, "enqueueRequest called.");
Objects.requireNonNull(transcodingRequest, "transcodingRequest must not be null");
Objects.requireNonNull(listenerExecutor, "listenerExecutor must not be null");
@@ -1208,7 +1283,14 @@
synchronized (mPendingTranscodingJobs) {
synchronized (mLock) {
if (mTranscodingClient == null) {
- // TODO(hkuang): Handle the case if client is temporarily unavailable.
+ // Try to register with the service again.
+ IMediaTranscodingService service = getService(false /*retry*/);
+ mTranscodingClient = registerClient(service);
+ // If still fails, throws an exception to tell client to try later.
+ if (mTranscodingClient == null) {
+ throw new MediaTranscodingException.ServiceNotAvailableException(
+ "Service rebooting. Try again later");
+ }
}
if (!mTranscodingClient.submitRequest(requestParcel, jobParcel)) {
@@ -1218,7 +1300,8 @@
// Wraps the TranscodingJobParcel into a TranscodingJob and returns it to client for
// tracking.
- TranscodingJob job = new TranscodingJob(mTranscodingClient, jobParcel,
+ TranscodingJob job = new TranscodingJob(this, transcodingRequest,
+ jobParcel,
listenerExecutor,
listener);
diff --git a/media/java/android/media/MediaTranscodingException.java b/media/java/android/media/MediaTranscodingException.java
new file mode 100644
index 0000000..50cc9c4
--- /dev/null
+++ b/media/java/android/media/MediaTranscodingException.java
@@ -0,0 +1,38 @@
+/*
+ * 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.media;
+
+/**
+ * Base class for MediaTranscoding exceptions
+ */
+public class MediaTranscodingException extends Exception {
+ private MediaTranscodingException(String detailMessage) {
+ super(detailMessage);
+ }
+
+ /**
+ * Exception thrown when the service is rebooting and MediaTranscodeManager is temporarily
+ * unavailable for accepting new request. It's likely that retrying will be successful.
+ */
+ public static final class ServiceNotAvailableException extends
+ MediaTranscodingException {
+ /** @hide */
+ public ServiceNotAvailableException(String detailMessage) {
+ super(detailMessage);
+ }
+ }
+}
diff --git a/media/java/android/media/MicrophoneInfo.java b/media/java/android/media/MicrophoneInfo.java
index 876628f..acd284f 100644
--- a/media/java/android/media/MicrophoneInfo.java
+++ b/media/java/android/media/MicrophoneInfo.java
@@ -268,8 +268,11 @@
/**
* Returns A {@link Coordinate3F} object that represents the geometric location of microphone
- * in meters, from bottom-left-back corner of appliance. X-axis, Y-axis and Z-axis show
- * as the x, y, z values.
+ * in meters. X-axis, Y-axis and Z-axis show as the x, y, z values. For mobile devices, the axes
+ * originate from the bottom-left-back corner of the appliance. In devices with
+ * {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE}, axes are defined with respect
+ * to the vehicle body frame, originating from the center of the vehicle's rear axle.
+ * @see <a href="https://source.android.com/devices/sensors/sensor-types#auto_axes">auto axes</a>
*
* @return the geometric location of the microphone or {@link #POSITION_UNKNOWN} if the
* geometric location is unknown
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 5db6729..5daf8b0 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -171,6 +171,12 @@
void DestroyCallback(const C2Buffer * /* buf */, void *arg) {
android::sp<android::MediaEvent> event = (android::MediaEvent *)arg;
+ if (event->mLinearBlockObj != NULL) {
+ JNIEnv *env = android::AndroidRuntime::getJNIEnv();
+ env->DeleteWeakGlobalRef(event->mLinearBlockObj);
+ event->mLinearBlockObj = NULL;
+ }
+
event->mAvHandleRefCnt--;
event->finalize();
}
@@ -182,6 +188,12 @@
mLnb = env->NewWeakGlobalRef(lnbObj);
}
+LnbCallback::~LnbCallback() {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ env->DeleteWeakGlobalRef(mLnb);
+ mLnb = NULL;
+}
+
Return<void> LnbCallback::onEvent(LnbEventType lnbEventType) {
ALOGD("LnbCallback::onEvent, type=%d", lnbEventType);
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -305,6 +317,7 @@
JNIEnv *env = AndroidRuntime::getJNIEnv();
mMediaEventObj = env->NewWeakGlobalRef(obj);
mAvHandle = native_handle_clone(avHandle.getNativeHandle());
+ mLinearBlockObj = NULL;
}
MediaEvent::~MediaEvent() {
@@ -367,7 +380,7 @@
true);
mLinearBlockObj = env->NewWeakGlobalRef(linearBlock);
mAvHandleRefCnt++;
- return mLinearBlockObj;
+ return linearBlock;
} else {
native_handle_close(const_cast<native_handle_t*>(
reinterpret_cast<const native_handle_t*>(mIonHandle)));
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index fd29959..c4deeaf 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -73,6 +73,7 @@
struct LnbCallback : public ILnbCallback {
LnbCallback(jweak tunerObj, LnbId id);
+ ~LnbCallback();
virtual Return<void> onEvent(LnbEventType lnbEventType);
virtual Return<void> onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage);
jweak mLnb;
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerDiedTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerDiedTest.java
index f00c14d..6b5abcc 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerDiedTest.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerDiedTest.java
@@ -22,6 +22,7 @@
import android.media.MediaTranscodeManager;
import android.media.MediaTranscodeManager.TranscodingJob;
import android.media.MediaTranscodeManager.TranscodingRequest;
+import android.media.MediaTranscodingException;
import android.net.Uri;
import android.os.Bundle;
import android.os.FileUtils;
@@ -250,6 +251,27 @@
TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertTrue("Invalid job status", job.getStatus() == TranscodingJob.STATUS_FINISHED);
assertTrue("Invalid job result", job.getResult()== TranscodingJob.RESULT_ERROR);
+
+
+ boolean retryJob = false;
+ // Wait till service is available again.
+ Log.d(TAG, "Retry the failed transcoding job");
+ while (!retryJob) {
+ try {
+ job.retry();
+ // Break out when job retry succeeds.
+ break;
+ } catch (MediaTranscodingException.ServiceNotAvailableException ex) {
+ // Sleep for 10 milliseconds to wait.
+ Thread.sleep(10);
+ }
+ }
+
+ 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);
}
}
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
index 73e1d98..10262d9 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
@@ -22,6 +22,7 @@
import android.media.MediaTranscodeManager;
import android.media.MediaTranscodeManager.TranscodingJob;
import android.media.MediaTranscodeManager.TranscodingRequest;
+import android.media.MediaTranscodingException;
import android.net.Uri;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
@@ -101,7 +102,8 @@
* Transcode the sourceFileName to destinationFileName with LOOP_COUNT.
*/
private void transcode(final String sourceFileName, final String destinationFileName)
- throws IOException, InterruptedException {
+ throws IOException, InterruptedException,
+ MediaTranscodingException.ServiceNotAvailableException {
AtomicLong totalTimeMs = new AtomicLong();
AtomicLong transcodingTime = new AtomicLong();
Uri srcUri = getUri(sourceFileName);
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index d62e132..1186c70 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -6188,7 +6188,7 @@
method public android.app.PictureInPictureParams build();
method public android.app.PictureInPictureParams.Builder setActions(java.util.List<android.app.RemoteAction>);
method public android.app.PictureInPictureParams.Builder setAspectRatio(android.util.Rational);
- method @NonNull public android.app.PictureInPictureParams.Builder setAutoEnterAllowed(boolean);
+ method @NonNull public android.app.PictureInPictureParams.Builder setAutoEnterEnabled(boolean);
method public android.app.PictureInPictureParams.Builder setSourceRectHint(android.graphics.Rect);
}
@@ -12061,6 +12061,7 @@
method public boolean getSyntheticAppDetailsActivityEnabled(@NonNull String);
method @NonNull public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
method @Nullable public abstract String[] getSystemSharedLibraryNames();
+ method @IntRange(from=0) public int getTargetSdkVersion(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract CharSequence getText(@NonNull String, @StringRes int, @Nullable android.content.pm.ApplicationInfo);
method @NonNull public abstract android.graphics.drawable.Drawable getUserBadgedDrawableForDensity(@NonNull android.graphics.drawable.Drawable, @NonNull android.os.UserHandle, @Nullable android.graphics.Rect, int);
method @NonNull public abstract android.graphics.drawable.Drawable getUserBadgedIcon(@NonNull android.graphics.drawable.Drawable, @NonNull android.os.UserHandle);
@@ -23907,6 +23908,7 @@
method @NonNull public java.util.List<java.lang.String> getAllProviders();
method @Nullable public String getBestProvider(@NonNull android.location.Criteria, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull String, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull String, @NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
method @NonNull public android.location.GnssCapabilities getGnssCapabilities();
method @Nullable public String getGnssHardwareModelName();
method public int getGnssYearOfHardware();
@@ -23941,6 +23943,8 @@
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(long, float, @NonNull android.location.Criteria, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull String, long, float, @NonNull android.app.PendingIntent);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(long, float, @NonNull android.location.Criteria, @NonNull android.app.PendingIntent);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull String, @NonNull android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull String, @NonNull android.location.LocationRequest, @NonNull android.app.PendingIntent);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull String, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull android.location.Criteria, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull String, @NonNull android.app.PendingIntent);
@@ -23984,6 +23988,30 @@
field @Deprecated public static final int TEMPORARILY_UNAVAILABLE = 1; // 0x1
}
+ public final class LocationRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=1) public long getDurationMillis();
+ method @IntRange(from=0) public long getIntervalMillis();
+ method @IntRange(from=1, to=java.lang.Integer.MAX_VALUE) public int getMaxUpdates();
+ method @FloatRange(from=0, to=java.lang.Float.MAX_VALUE) public float getMinUpdateDistanceMeters();
+ method @IntRange(from=0) public long getMinUpdateIntervalMillis();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.LocationRequest> CREATOR;
+ field public static final long PASSIVE_INTERVAL = 9223372036854775807L; // 0x7fffffffffffffffL
+ }
+
+ public static final class LocationRequest.Builder {
+ ctor public LocationRequest.Builder(long);
+ ctor public LocationRequest.Builder(@NonNull android.location.LocationRequest);
+ method @NonNull public android.location.LocationRequest build();
+ method @NonNull public android.location.LocationRequest.Builder clearMinUpdateIntervalMillis();
+ method @NonNull public android.location.LocationRequest.Builder setDurationMillis(@IntRange(from=1) long);
+ method @NonNull public android.location.LocationRequest.Builder setIntervalMillis(@IntRange(from=0) long);
+ method @NonNull public android.location.LocationRequest.Builder setMaxUpdates(@IntRange(from=1, to=java.lang.Integer.MAX_VALUE) int);
+ method @NonNull public android.location.LocationRequest.Builder setMinUpdateDistanceMeters(@FloatRange(from=0, to=java.lang.Float.MAX_VALUE) float);
+ method @NonNull public android.location.LocationRequest.Builder setMinUpdateIntervalMillis(@IntRange(from=0) long);
+ }
+
public interface OnNmeaMessageListener {
method public void onNmeaMessage(String, long);
}
@@ -27111,6 +27139,12 @@
field public static final android.media.MediaTimestamp TIMESTAMP_UNKNOWN;
}
+ public class MediaTranscodingException extends java.lang.Exception {
+ }
+
+ public static final class MediaTranscodingException.ServiceNotAvailableException extends android.media.MediaTranscodingException {
+ }
+
public interface MicrophoneDirection {
method public boolean setPreferredMicrophoneDirection(int);
method public boolean setPreferredMicrophoneFieldDimension(@FloatRange(from=-1.0, to=1.0) float);
@@ -44213,6 +44247,8 @@
field public static final int MISSED = 5; // 0x5
field public static final int OTHER = 9; // 0x9
field public static final String REASON_EMERGENCY_CALL_PLACED = "REASON_EMERGENCY_CALL_PLACED";
+ field public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
+ field public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
field public static final int REJECTED = 6; // 0x6
field public static final int REMOTE = 3; // 0x3
field public static final int RESTRICTED = 8; // 0x8
@@ -44307,6 +44343,7 @@
field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
field public static final int CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE = 256; // 0x100
field @NonNull public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
+ field public static final String EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE = "android.telecom.extra.ALWAYS_USE_VOIP_AUDIO_MODE";
field public static final String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING = "android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING";
field public static final String EXTRA_CALL_SUBJECT_MAX_LENGTH = "android.telecom.extra.CALL_SUBJECT_MAX_LENGTH";
field public static final String EXTRA_LOG_SELF_MANAGED_CALLS = "android.telecom.extra.LOG_SELF_MANAGED_CALLS";
@@ -44873,6 +44910,7 @@
field public static final String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
field public static final String KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY = "apn_settings_default_apn_types_string_array";
field public static final String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool";
+ field public static final String KEY_CALL_BARRING_DEFAULT_SERVICE_CLASS_INT = "call_barring_default_service_class_int";
field public static final String KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL = "call_barring_supports_deactivate_all_bool";
field public static final String KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL = "call_barring_supports_password_change_bool";
field public static final String KEY_CALL_BARRING_VISIBILITY_BOOL = "call_barring_visibility_bool";
@@ -45100,6 +45138,8 @@
field public static final String KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING = "wfc_emergency_address_carrier_app_string";
field public static final String KEY_WORLD_MODE_ENABLED_BOOL = "world_mode_enabled_bool";
field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
+ field public static final int SERVICE_CLASS_NONE = 0; // 0x0
+ field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
}
public static final class CarrierConfigManager.Apn {
diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index 63b98cb..ae576d4 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -10,6 +10,10 @@
field public static final String ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED = "android.app.action.NOTIFICATION_LISTENER_ENABLED_CHANGED";
}
+ public class StatusBarManager {
+ method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSimNetworkLock(boolean);
+ }
+
}
package android.content.rollback {
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index 8fae240..82f2021 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -671,6 +671,9 @@
method @Nullable public android.content.ComponentName getAllowedNotificationAssistant();
method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName);
method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean);
+ field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL";
+ field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL";
+ field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL";
}
public final class RuntimeAppOpAccessMessage implements android.os.Parcelable {
@@ -1529,12 +1532,16 @@
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setBluetoothTethering(boolean);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
+ field public static final String ACTION_TETHERING_STATE_CHANGED = "android.bluetooth.action.TETHERING_STATE_CHANGED";
field public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE";
+ field public static final String EXTRA_TETHERING_STATE = "android.bluetooth.extra.TETHERING_STATE";
field public static final int LOCAL_NAP_ROLE = 1; // 0x1
field public static final int LOCAL_PANU_ROLE = 2; // 0x2
field public static final int PAN_ROLE_NONE = 0; // 0x0
field public static final int REMOTE_NAP_ROLE = 1; // 0x1
field public static final int REMOTE_PANU_ROLE = 2; // 0x2
+ field public static final int TETHERING_STATE_OFF = 1; // 0x1
+ field public static final int TETHERING_STATE_ON = 2; // 0x2
}
public class BluetoothPbap implements android.bluetooth.BluetoothProfile {
@@ -4015,7 +4022,7 @@
public class LocationManager {
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch();
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
method @Nullable public String getExtraLocationControllerPackage();
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int getGnssBatchSize();
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void injectGnssMeasurementCorrections(@NonNull android.location.GnssMeasurementCorrections);
@@ -4026,9 +4033,9 @@
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.LOCATION_HARDWARE}) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackage(@Nullable String);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackageEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle);
@@ -4037,42 +4044,49 @@
}
public final class LocationRequest implements android.os.Parcelable {
- method @NonNull public static android.location.LocationRequest create();
- method @NonNull public static android.location.LocationRequest createFromDeprecatedCriteria(@NonNull android.location.Criteria, long, float, boolean);
- method @NonNull public static android.location.LocationRequest createFromDeprecatedProvider(@NonNull String, long, float, boolean);
- method public int describeContents();
+ method @Deprecated @NonNull public static android.location.LocationRequest create();
+ method @Deprecated @NonNull public static android.location.LocationRequest createFromDeprecatedCriteria(@NonNull android.location.Criteria, long, float, boolean);
+ method @Deprecated @NonNull public static android.location.LocationRequest createFromDeprecatedProvider(@NonNull String, long, float, boolean);
method @Deprecated public long getExpireAt();
- method public long getExpireIn();
- method public long getFastestInterval();
- method public boolean getHideFromAppOps();
- method public long getInterval();
- method public int getNumUpdates();
- method @NonNull public String getProvider();
+ method @Deprecated public long getExpireIn();
+ method @Deprecated public long getFastestInterval();
+ method @Deprecated public boolean getHideFromAppOps();
+ method @Deprecated public long getInterval();
+ method @Deprecated public int getNumUpdates();
+ method @Deprecated @NonNull public String getProvider();
method public int getQuality();
- method public float getSmallestDisplacement();
+ method @Deprecated public float getSmallestDisplacement();
method @Nullable public android.os.WorkSource getWorkSource();
+ method public boolean isHiddenFromAppOps();
method public boolean isLocationSettingsIgnored();
- method public boolean isLowPowerMode();
+ method public boolean isLowPower();
+ method @Deprecated public boolean isLowPowerMode();
method @Deprecated @NonNull public android.location.LocationRequest setExpireAt(long);
- method @NonNull public android.location.LocationRequest setExpireIn(long);
- method @NonNull public android.location.LocationRequest setFastestInterval(long);
- method public void setHideFromAppOps(boolean);
- method @NonNull public android.location.LocationRequest setInterval(long);
- method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest setLocationSettingsIgnored(boolean);
- method @NonNull public android.location.LocationRequest setLowPowerMode(boolean);
- method @NonNull public android.location.LocationRequest setNumUpdates(int);
- method @NonNull public android.location.LocationRequest setProvider(@NonNull String);
- method @NonNull public android.location.LocationRequest setQuality(int);
- method @NonNull public android.location.LocationRequest setSmallestDisplacement(float);
- method public void setWorkSource(@Nullable android.os.WorkSource);
- method public void writeToParcel(android.os.Parcel, int);
+ method @Deprecated @NonNull public android.location.LocationRequest setExpireIn(long);
+ method @Deprecated @NonNull public android.location.LocationRequest setFastestInterval(long);
+ method @Deprecated public void setHideFromAppOps(boolean);
+ method @Deprecated @NonNull public android.location.LocationRequest setInterval(long);
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest setLocationSettingsIgnored(boolean);
+ method @Deprecated @NonNull public android.location.LocationRequest setLowPowerMode(boolean);
+ method @Deprecated @NonNull public android.location.LocationRequest setNumUpdates(int);
+ method @Deprecated @NonNull public android.location.LocationRequest setProvider(@NonNull String);
+ method @Deprecated @NonNull public android.location.LocationRequest setQuality(int);
+ method @Deprecated @NonNull public android.location.LocationRequest setSmallestDisplacement(float);
+ method @Deprecated public void setWorkSource(@Nullable android.os.WorkSource);
field public static final int ACCURACY_BLOCK = 102; // 0x66
field public static final int ACCURACY_CITY = 104; // 0x68
field public static final int ACCURACY_FINE = 100; // 0x64
- field @NonNull public static final android.os.Parcelable.Creator<android.location.LocationRequest> CREATOR;
field public static final int POWER_HIGH = 203; // 0xcb
field public static final int POWER_LOW = 201; // 0xc9
- field public static final int POWER_NONE = 200; // 0xc8
+ field @Deprecated public static final int POWER_NONE = 200; // 0xc8
+ }
+
+ public static final class LocationRequest.Builder {
+ method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setLocationSettingsIgnored(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.location.LocationRequest.Builder setLowPower(boolean);
+ method @NonNull public android.location.LocationRequest.Builder setQuality(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
}
}
@@ -4293,7 +4307,7 @@
}
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;
+ 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;
field public static final int PRIORITY_REALTIME = 1; // 0x1
field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1
}
@@ -4308,7 +4322,6 @@
method @IntRange(from=0, to=100) public int getProgress();
method public int getResult();
method public int getStatus();
- method public boolean retry();
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
@@ -8228,6 +8241,7 @@
field public static final String APN_SET_ID = "apn_set_id";
field public static final int CARRIER_EDITED = 4; // 0x4
field public static final String EDITED_STATUS = "edited";
+ field public static final int MATCH_ALL_APN_SET_ID = -1; // 0xffffffff
field public static final String MAX_CONNECTIONS = "max_conns";
field public static final String MODEM_PERSIST = "modem_cognitive";
field public static final String MTU = "mtu";
@@ -9239,6 +9253,10 @@
method public final void addExistingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.Connection, @NonNull android.telecom.Conference);
}
+ public final class DisconnectCause implements android.os.Parcelable {
+ field public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
+ }
+
public abstract class InCallService extends android.app.Service {
method @Deprecated public android.telecom.Phone getPhone();
method @Deprecated public void onPhoneCreated(android.telecom.Phone);
@@ -9367,7 +9385,12 @@
}
public final class PhoneAccount implements android.os.Parcelable {
+ field public static final int CAPABILITY_EMERGENCY_CALLS_ONLY = 128; // 0x80
+ field public static final int CAPABILITY_EMERGENCY_PREFERRED = 8192; // 0x2000
+ field public static final int CAPABILITY_EMERGENCY_VIDEO_CALLING = 512; // 0x200
field public static final int CAPABILITY_MULTI_USER = 32; // 0x20
+ field public static final String EXTRA_PLAY_CALL_RECORDING_TONE = "android.telecom.extra.PLAY_CALL_RECORDING_TONE";
+ field public static final String EXTRA_SORT_ORDER = "android.telecom.extra.SORT_ORDER";
}
public static class PhoneAccount.Builder {
@@ -9453,10 +9476,20 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
method @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 void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle);
+ field public static final String ACTION_CURRENT_TTY_MODE_CHANGED = "android.telecom.action.CURRENT_TTY_MODE_CHANGED";
+ field public static final String ACTION_TTY_PREFERRED_MODE_CHANGED = "android.telecom.action.TTY_PREFERRED_MODE_CHANGED";
+ field public static final int CALL_SOURCE_EMERGENCY_DIALPAD = 1; // 0x1
+ field public static final int CALL_SOURCE_EMERGENCY_SHORTCUT = 2; // 0x2
+ field public static final int CALL_SOURCE_UNSPECIFIED = 0; // 0x0
field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
+ field public static final String EXTRA_CALL_SOURCE = "android.telecom.extra.CALL_SOURCE";
+ field public static final String EXTRA_CALL_TECHNOLOGY_TYPE = "android.telecom.extra.CALL_TECHNOLOGY_TYPE";
field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
field public static final String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
+ field public static final String EXTRA_CURRENT_TTY_MODE = "android.telecom.extra.CURRENT_TTY_MODE";
field public static final String EXTRA_IS_USER_INTENT_EMERGENCY_CALL = "android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL";
+ field public static final String EXTRA_TTY_PREFERRED_MODE = "android.telecom.extra.TTY_PREFERRED_MODE";
+ field public static final String EXTRA_UNKNOWN_CALL_HANDLE = "android.telecom.extra.UNKNOWN_CALL_HANDLE";
field public static final int TTY_MODE_FULL = 1; // 0x1
field public static final int TTY_MODE_HCO = 2; // 0x2
field public static final int TTY_MODE_OFF = 0; // 0x0
@@ -9766,7 +9799,8 @@
method public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
- method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
+ method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
+ method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
method public void onPhysicalChannelConfigurationChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
method public void onRadioPowerStateChanged(int);
@@ -10385,6 +10419,7 @@
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();
@@ -10396,6 +10431,11 @@
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 = 2; // 0x2
+ field public static final int HANDOVER_FAILURE_MODE_LEGACY = 1; // 0x1
+ field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER = 3; // 0x3
+ field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 4; // 0x4
+ field public static final int HANDOVER_FAILURE_MODE_UNKNOWN = 0; // 0x0
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
@@ -10409,6 +10449,7 @@
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);
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml
index 8b235e6..d57875f 100644
--- a/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -53,7 +53,7 @@
android:textColor="?attr/wallpaperTextColor"
android:textAppearance="?android:attr/textAppearanceMedium"
android:imeOptions="flagForceAscii|actionDone"
- android:maxLength="@integer/password_text_view_scale"
+ android:maxLength="@integer/password_max_length"
/>
<TextView
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java
index 0b3ac2a..5b0bb35 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java
@@ -254,6 +254,28 @@
}
@Test
+ public void testSetTopWindowVisibility_setTrue_isVisible() {
+ mTestableResources.addOverride(R.bool.config_enableTopNavigationBar, true);
+ mCarNavigationBar = createNavigationBarController();
+
+ ViewGroup window = mCarNavigationBar.getTopWindow();
+ mCarNavigationBar.setTopWindowVisibility(View.VISIBLE);
+
+ assertThat(window.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void testSetTopWindowVisibility_setFalse_isGone() {
+ mTestableResources.addOverride(R.bool.config_enableTopNavigationBar, true);
+ mCarNavigationBar = createNavigationBarController();
+
+ ViewGroup window = mCarNavigationBar.getTopWindow();
+ mCarNavigationBar.setTopWindowVisibility(View.GONE);
+
+ assertThat(window.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
public void testSetBottomWindowVisibility_setTrue_isVisible() {
mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
mCarNavigationBar = createNavigationBarController();
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index 781efc1..900e68d 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -30,7 +30,6 @@
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationRequest;
-import android.os.Looper;
import android.os.WorkSource;
import com.android.internal.annotations.GuardedBy;
@@ -182,6 +181,7 @@
for (LocationRequestUnbundled request : mRequest.getLocationRequests()) {
switch (request.getQuality()) {
case LocationRequestUnbundled.ACCURACY_FINE:
+ case LocationRequestUnbundled.ACCURACY_BLOCK:
case LocationRequestUnbundled.POWER_HIGH:
if (request.getInterval() < gpsInterval) {
gpsInterval = request.getInterval();
@@ -190,7 +190,6 @@
networkInterval = request.getInterval();
}
break;
- case LocationRequestUnbundled.ACCURACY_BLOCK:
case LocationRequestUnbundled.ACCURACY_CITY:
case LocationRequestUnbundled.POWER_LOW:
if (request.getInterval() < networkInterval) {
@@ -219,13 +218,12 @@
mLocationManager.removeUpdates(listener);
}
if (newInterval != Long.MAX_VALUE) {
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(
- provider, newInterval, 0, false);
- if (mRequest.isLocationSettingsIgnored()) {
- request.setLocationSettingsIgnored(true);
- }
- request.setWorkSource(mWorkSource);
- mLocationManager.requestLocationUpdates(request, listener, Looper.getMainLooper());
+ LocationRequest request = new LocationRequest.Builder(newInterval)
+ .setLocationSettingsIgnored(mRequest.isLocationSettingsIgnored())
+ .setWorkSource(mWorkSource)
+ .build();
+ mLocationManager.requestLocationUpdates(provider, request, mContext.getMainExecutor(),
+ listener);
}
}
diff --git a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
index 2c4545e..e05bd3c 100644
--- a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
+++ b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
@@ -16,7 +16,6 @@
package com.android.location.fused.tests;
-import static android.location.LocationManager.FUSED_PROVIDER;
import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationManager.NETWORK_PROVIDER;
@@ -121,12 +120,11 @@
@Test
public void testNetworkRequest() throws Exception {
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(FUSED_PROVIDER, 1000,
- 0, false);
+ LocationRequest request = new LocationRequest.Builder(1000).build();
mProvider.setRequest(
new ProviderRequest.Builder()
- .setInterval(1000)
+ .setIntervalMillis(1000)
.setLocationRequests(Collections.singletonList(request))
.build(),
new WorkSource());
@@ -139,12 +137,13 @@
@Test
public void testGpsRequest() throws Exception {
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(FUSED_PROVIDER, 1000,
- 0, false).setQuality(LocationRequest.POWER_HIGH);
+ LocationRequest request = new LocationRequest.Builder(1000)
+ .setQuality(LocationRequest.POWER_HIGH)
+ .build();
mProvider.setRequest(
new ProviderRequest.Builder()
- .setInterval(1000)
+ .setIntervalMillis(1000)
.setLocationRequests(Collections.singletonList(request))
.build(),
new WorkSource());
diff --git a/packages/InputDevices/res/raw/keyboard_layout_bulgarian_phonetic.kcm b/packages/InputDevices/res/raw/keyboard_layout_bulgarian_phonetic.kcm
new file mode 100644
index 0000000..8878807
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_bulgarian_phonetic.kcm
@@ -0,0 +1,407 @@
+# 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.
+
+#
+# Bulgarian keyboard layout.
+# This is a typical Bulgarian PC keyboard layout.
+# As an added convenience, English characters are accessible using ralt (Alt Gr).
+#
+
+type OVERLAY
+
+map key 86 PLUS
+
+### ROW 1
+
+key GRAVE {
+ label: '`'
+ base: '\u044e'
+ shift, capslock: '\u042e'
+ ralt: '`'
+ ralt+shift: '~'
+}
+
+key 1 {
+ label: '1'
+ base: '1'
+ shift: '!'
+ ralt: '!'
+}
+
+key 2 {
+ label: '2'
+ base: '2'
+ shift: '@'
+ ralt: '@'
+}
+
+key 3 {
+ label: '3'
+ base: '3'
+ shift: '\u2116'
+ ralt: '#'
+}
+
+key 4 {
+ label: '4'
+ base: '4'
+ shift: '$'
+ ralt: '$'
+}
+
+key 5 {
+ label: '5'
+ base: '5'
+ shift: '%'
+ ralt: '%'
+}
+
+key 6 {
+ label: '6'
+ base: '6'
+ shift: '\u20ac'
+ ralt: '^'
+}
+
+key 7 {
+ label: '7'
+ base: '7'
+ shift: '\u00a7'
+ ralt: '&'
+}
+
+key 8 {
+ label: '8'
+ base: '8'
+ shift: '*'
+ ralt: '*'
+}
+
+key 9 {
+ label: '9'
+ base: '9'
+ shift: '('
+ ralt: '('
+}
+
+key 0 {
+ label: '0'
+ base: '0'
+ shift: ')'
+ ralt: ')'
+}
+
+key MINUS {
+ label: '-'
+ base: '-'
+ shift: '\u2013'
+ ralt: '_'
+}
+
+key EQUALS {
+ label: '='
+ base: '='
+ shift: '+'
+ ralt: '='
+ ralt+shift: '+'
+}
+
+### ROW 2
+
+key Q {
+ label: '\u0447'
+ base: '\u0447'
+ shift: '\u0427'
+ capslock: '\u0427'
+ ralt: 'q'
+ ralt+shift, ralt+capslock: 'Q'
+}
+
+key W {
+ label: '\u0448'
+ base: '\u0448'
+ shift, capslock: '\u0428'
+ ralt: 'w'
+ ralt+shift, ralt+capslock: 'W'
+}
+
+key E {
+ label: '\u0435'
+ base: '\u0435'
+ shift, capslock: '\u0415'
+ ralt: 'e'
+ ralt+shift, ralt+capslock: 'E'
+}
+
+key R {
+ label: '\u0440'
+ base: '\u0440'
+ shift, capslock: '\u0420'
+ ralt: 'r'
+ ralt+shift, ralt+capslock: 'R'
+}
+
+key T {
+ label: '\u0442'
+ base: '\u0442'
+ shift, capslock: '\u0422'
+ ralt: 't'
+ ralt+shift, ralt+capslock: 'T'
+}
+
+key Y {
+ label: '\u044a'
+ base: '\u044a'
+ shift, capslock: '\u042a'
+ ralt: 'y'
+ ralt+shift, ralt+capslock: 'Y'
+}
+
+key U {
+ label: '\u0443'
+ base: '\u0443'
+ shift, capslock: '\u0423'
+ ralt: 'u'
+ ralt+shift, ralt+capslock: 'U'
+}
+
+key I {
+ label: '\u0438'
+ base: '\u0438'
+ shift, capslock: '\u0418'
+ ralt: 'i'
+ ralt+shift, ralt+capslock: 'I'
+}
+
+key O {
+ label: '\u043e'
+ base: '\u043e'
+ shift, capslock: '\u041e'
+ ralt: 'o'
+ ralt+shift, ralt+capslock: 'O'
+}
+
+key P {
+ label: '\u043f'
+ base: '\u043f'
+ shift, capslock: '\u041f'
+ ralt: 'p'
+ ralt+shift, ralt+capslock: 'P'
+}
+
+key LEFT_BRACKET {
+ label: '\u044f'
+ base: '\u044f'
+ shift, capslock: '\u042f'
+ ralt: '['
+ ralt+shift: '{'
+}
+
+key RIGHT_BRACKET {
+ label: '\u0449'
+ base: '\u0449'
+ shift: '\u0429'
+ ralt: ']'
+ ralt+shift: '}'
+}
+
+key BACKSLASH {
+ label: '\u044c'
+ base: '\u044c'
+ shift: '\u042c'
+ capslock: '\u042c'
+ shift+capslock: '\u040d'
+ ralt: '\\'
+ ralt+shift: '|'
+}
+
+### ROW 3
+
+key A {
+ label: '\u0430'
+ base: '\u0430'
+ shift, capslock: '\u0410'
+ ralt: 'a'
+ ralt+shift, ralt+capslock: 'A'
+}
+
+key S {
+ label: '\u0441'
+ base: '\u0441'
+ shift, capslock: '\u0421'
+ ralt: 's'
+ ralt+shift, ralt+capslock: 'S'
+}
+
+key D {
+ label: '\u0434'
+ base: '\u0434'
+ shift, capslock: '\u0414'
+ ralt: 'd'
+ ralt+shift, ralt+capslock: 'D'
+}
+
+key F {
+ label: '\u0444'
+ base: '\u0444'
+ shift, capslock: '\u0424'
+ ralt: 'f'
+ ralt+shift, ralt+capslock: 'F'
+}
+
+key G {
+ label: '\u0433'
+ base: '\u0433'
+ shift, capslock: '\u0413'
+ ralt: 'g'
+ ralt+shift, ralt+capslock: 'G'
+}
+
+key H {
+ label: '\u0445'
+ base: '\u0445'
+ shift, capslock: '\u0425'
+ ralt: 'h'
+ ralt+shift, ralt+capslock: 'H'
+}
+
+key J {
+ label: '\u0439'
+ base: '\u0439'
+ shift, capslock: '\u0419'
+ ralt: 'j'
+ ralt+shift, ralt+capslock: 'J'
+}
+
+key K {
+ label: '\u043a'
+ base: '\u043a'
+ shift, capslock: '\u041a'
+ ralt: 'k'
+ ralt+shift, ralt+capslock: 'K'
+}
+
+key L {
+ label: '\u043b'
+ base: '\u043b'
+ shift, capslock: '\u041b'
+ ralt: 'l'
+ ralt+shift, ralt+capslock: 'L'
+}
+
+key SEMICOLON {
+ label: ';'
+ base: ';'
+ shift, capslock: ':'
+ ralt: ';'
+ ralt+shift: ':'
+}
+
+key APOSTROPHE {
+ label: '\''
+ base: '\''
+ shift, capslock: '"'
+ ralt: '\''
+ ralt+shift: '"'
+}
+
+### ROW 4
+
+key PLUS {
+ label: '\u045d'
+ base: '\u045d'
+ shift, capslock: '\u040d'
+ ralt: '\\'
+ ralt+shift: '|'
+}
+
+key Z {
+ label: '\u0437'
+ base: '\u0437'
+ shift, capslock: '\u0417'
+ ralt: 'z'
+ ralt+shift, ralt+capslock: 'Z'
+}
+
+key X {
+ label: '\u0436'
+ base: '\u0436'
+ shift, capslock: '\u0416'
+ ralt: 'x'
+ ralt+shift, ralt+capslock: 'X'
+}
+
+key C {
+ label: '\u0446'
+ base: '\u0446'
+ shift, capslock: '\u0426'
+ ralt: 'c'
+ ralt+shift, ralt+capslock: 'C'
+}
+
+key V {
+ label: '\u0432'
+ base: '\u0432'
+ shift, capslock: '\u0412'
+ ralt: 'v'
+ ralt+shift, ralt+capslock: 'V'
+}
+
+key B {
+ label: '\u0431'
+ base: '\u0431'
+ shift, capslock: '\u0411'
+ ralt: 'b'
+ ralt+shift, ralt+capslock: 'B'
+}
+
+key N {
+ label: '\u043d'
+ base: '\u043d'
+ shift, capslock: '\u041d'
+ ralt: 'n'
+ ralt+shift, ralt+capslock: 'N'
+}
+
+key M {
+ label: '\u043c'
+ base: '\u043c'
+ shift, capslock: '\u041c'
+ ralt: 'm'
+ ralt+shift, ralt+capslock: 'M'
+}
+
+key COMMA {
+ label: ','
+ base: ','
+ shift, capslock: '\u201e'
+ ralt: ','
+ ralt+shift: '<'
+}
+
+key PERIOD {
+ label: '.'
+ base: '.'
+ shift, capslock: '\u201c'
+ ralt: '.'
+ ralt+shift: '>'
+}
+
+key SLASH {
+ label: '/'
+ base: '/'
+ shift, capslock: '?'
+ ralt: '/'
+ ralt+shift: '?'
+}
diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml
index f3e11fb..eb89ee5 100644
--- a/packages/InputDevices/res/values-af/strings.xml
+++ b/packages/InputDevices/res/values-af/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Fins"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroaties"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Tsjeggies"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Tsjeggiese QWERTY-styl"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estnies"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hongaars"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Yslands"</string>
diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml
index 1729f29..2970bfe 100644
--- a/packages/InputDevices/res/values-am/strings.xml
+++ b/packages/InputDevices/res/values-am/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"ፊኒሽ"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"ክሮሽያ"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"ቼክ"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"የቼክኛ QWERTY ቅጥ"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"ኤስቶኒያ"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ሀንጋሪ"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"አይስላንድ"</string>
diff --git a/packages/InputDevices/res/values-bg/strings.xml b/packages/InputDevices/res/values-bg/strings.xml
index 01feb95..a1edb21 100644
--- a/packages/InputDevices/res/values-bg/strings.xml
+++ b/packages/InputDevices/res/values-bg/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"финландски"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"хърватски"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"чешки"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Чешки стил за QWERTY"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"естонски"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"унгарски"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"исландски"</string>
diff --git a/packages/InputDevices/res/values-bs/strings.xml b/packages/InputDevices/res/values-bs/strings.xml
index 893d453..436a3b0 100644
--- a/packages/InputDevices/res/values-bs/strings.xml
+++ b/packages/InputDevices/res/values-bs/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"finski"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"hrvatski"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"češki"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Češka QWERTY tastatura"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"estonski"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"mađarski"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"islandski"</string>
diff --git a/packages/InputDevices/res/values-ca/strings.xml b/packages/InputDevices/res/values-ca/strings.xml
index 9da74e1..1f089e1 100644
--- a/packages/InputDevices/res/values-ca/strings.xml
+++ b/packages/InputDevices/res/values-ca/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finès"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croat"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Txec"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Estil QWERTY txec"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonià"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hongarès"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandès"</string>
diff --git a/packages/InputDevices/res/values-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml
index 041fcbb..b36861a 100644
--- a/packages/InputDevices/res/values-cs/strings.xml
+++ b/packages/InputDevices/res/values-cs/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"finské"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"chorvatské"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"české"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Český styl QWERTY"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"estonské"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"maďarské"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"islandské"</string>
diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml
index bde1fd2..7e446fd 100644
--- a/packages/InputDevices/res/values-da/strings.xml
+++ b/packages/InputDevices/res/values-da/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finsk"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroatisk"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Tjekkisk"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Tjekkisk – QWERTY-layout"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estisk"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Ungarsk"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandsk"</string>
diff --git a/packages/InputDevices/res/values-de/strings.xml b/packages/InputDevices/res/values-de/strings.xml
index 975308f..c8a7ab1 100644
--- a/packages/InputDevices/res/values-de/strings.xml
+++ b/packages/InputDevices/res/values-de/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnisch"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroatisch"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Tschechisch"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"QWERTY-Tastatur, tschechisch"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estnisch"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Ungarisch"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Isländisch"</string>
diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml
index 52088f6..ff7cd02 100644
--- a/packages/InputDevices/res/values-el/strings.xml
+++ b/packages/InputDevices/res/values-el/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Φινλανδικά"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Κροατικά"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Τσεχικά"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Τσεχικό πληκτρολόγιο στιλ QWERTY"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Εσθονικά"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Ουγγρικά"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Ισλανδικά"</string>
diff --git a/packages/InputDevices/res/values-en-rAU/strings.xml b/packages/InputDevices/res/values-en-rAU/strings.xml
index 17a1fb7..02a8e6d 100644
--- a/packages/InputDevices/res/values-en-rAU/strings.xml
+++ b/packages/InputDevices/res/values-en-rAU/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnish"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croatian"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Czech"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Czech QWERTY style"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonian"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hungarian"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Icelandic"</string>
diff --git a/packages/InputDevices/res/values-en-rCA/strings.xml b/packages/InputDevices/res/values-en-rCA/strings.xml
index 17a1fb7..02a8e6d 100644
--- a/packages/InputDevices/res/values-en-rCA/strings.xml
+++ b/packages/InputDevices/res/values-en-rCA/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnish"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croatian"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Czech"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Czech QWERTY style"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonian"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hungarian"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Icelandic"</string>
diff --git a/packages/InputDevices/res/values-en-rGB/strings.xml b/packages/InputDevices/res/values-en-rGB/strings.xml
index 17a1fb7..02a8e6d 100644
--- a/packages/InputDevices/res/values-en-rGB/strings.xml
+++ b/packages/InputDevices/res/values-en-rGB/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnish"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croatian"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Czech"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Czech QWERTY style"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonian"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hungarian"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Icelandic"</string>
diff --git a/packages/InputDevices/res/values-en-rIN/strings.xml b/packages/InputDevices/res/values-en-rIN/strings.xml
index 17a1fb7..02a8e6d 100644
--- a/packages/InputDevices/res/values-en-rIN/strings.xml
+++ b/packages/InputDevices/res/values-en-rIN/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnish"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croatian"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Czech"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Czech QWERTY style"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonian"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hungarian"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Icelandic"</string>
diff --git a/packages/InputDevices/res/values-en-rXC/strings.xml b/packages/InputDevices/res/values-en-rXC/strings.xml
index 8603c54..a8d342f 100644
--- a/packages/InputDevices/res/values-en-rXC/strings.xml
+++ b/packages/InputDevices/res/values-en-rXC/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnish"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croatian"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Czech"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Czech QWERTY style"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonian"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hungarian"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Icelandic"</string>
diff --git a/packages/InputDevices/res/values-es-rUS/strings.xml b/packages/InputDevices/res/values-es-rUS/strings.xml
index fc5e3d8..9090cb7 100644
--- a/packages/InputDevices/res/values-es-rUS/strings.xml
+++ b/packages/InputDevices/res/values-es-rUS/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandés"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croata"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Checo"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"QWERTY estilo checo"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonio"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Húngaro"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandés"</string>
diff --git a/packages/InputDevices/res/values-es/strings.xml b/packages/InputDevices/res/values-es/strings.xml
index 773cdf9..66ea655 100644
--- a/packages/InputDevices/res/values-es/strings.xml
+++ b/packages/InputDevices/res/values-es/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandés"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croata"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Checo"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Estilo QWERTY checo"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonio"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Húngaro"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandés"</string>
diff --git a/packages/InputDevices/res/values-et/strings.xml b/packages/InputDevices/res/values-et/strings.xml
index dbe5865..9674a0d 100644
--- a/packages/InputDevices/res/values-et/strings.xml
+++ b/packages/InputDevices/res/values-et/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Soome"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Horvaatia"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Tšehhi"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Tšehhi QWERTY-stiil"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Eesti"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Ungari"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandi"</string>
diff --git a/packages/InputDevices/res/values-eu/strings.xml b/packages/InputDevices/res/values-eu/strings.xml
index e89ffa8..357b618 100644
--- a/packages/InputDevices/res/values-eu/strings.xml
+++ b/packages/InputDevices/res/values-eu/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandiarra"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroaziarra"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Txekiarra"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"QWERTY estilo txekiarra"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estoniarra"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hungariarra"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandiarra"</string>
diff --git a/packages/InputDevices/res/values-fa/strings.xml b/packages/InputDevices/res/values-fa/strings.xml
index 2d5f81d..5395d27 100644
--- a/packages/InputDevices/res/values-fa/strings.xml
+++ b/packages/InputDevices/res/values-fa/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"فنلاندی"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"کرواسی"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"چک"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"سبک QWERTY چک"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"استونیایی"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"مجارستانی"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"ایسلندی"</string>
diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml
index beb0e07..e714e83 100644
--- a/packages/InputDevices/res/values-fr-rCA/strings.xml
+++ b/packages/InputDevices/res/values-fr-rCA/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnois"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croate"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Tchèque"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Clavier QWERTY tchèque"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonien"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hongrois"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandais"</string>
diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml
index 223ed3c..0a022f1 100644
--- a/packages/InputDevices/res/values-fr/strings.xml
+++ b/packages/InputDevices/res/values-fr/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnois"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croate"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Tchèque"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Clavier QWERTY tchèque"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonien"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hongrois"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandais"</string>
diff --git a/packages/InputDevices/res/values-gl/strings.xml b/packages/InputDevices/res/values-gl/strings.xml
index 7d3e1d6..0c86f81 100644
--- a/packages/InputDevices/res/values-gl/strings.xml
+++ b/packages/InputDevices/res/values-gl/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finés"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croata"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Checo"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Estilo QWERTY checo"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estoniano"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Húngaro"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandés"</string>
diff --git a/packages/InputDevices/res/values-gu/strings.xml b/packages/InputDevices/res/values-gu/strings.xml
index 0f7863a..8648389 100644
--- a/packages/InputDevices/res/values-gu/strings.xml
+++ b/packages/InputDevices/res/values-gu/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"ફિનિશ"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"ક્રોએશિયન"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"ચેક"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"ચેક QWERTY શૈલી"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"એસ્ટોનિયન"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"હંગેરિયન"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"આઇસલેન્ડિક"</string>
diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml
index 37a5e58..6e674ed 100644
--- a/packages/InputDevices/res/values-hi/strings.xml
+++ b/packages/InputDevices/res/values-hi/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"फ़िनिश"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"क्रोएशियन"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"चेक"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"चेक QWERTY स्टाइल"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"एस्टोनियाई"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"हंगेरियाई"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"आइसलैंडिक"</string>
diff --git a/packages/InputDevices/res/values-hr/strings.xml b/packages/InputDevices/res/values-hr/strings.xml
index 83c2fc1..cd2dcc1 100644
--- a/packages/InputDevices/res/values-hr/strings.xml
+++ b/packages/InputDevices/res/values-hr/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"finska"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"hrvatska"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"češka"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Češka QWERTY tipkovnica"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"estonska"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"mađarska"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"islandska"</string>
diff --git a/packages/InputDevices/res/values-hu/strings.xml b/packages/InputDevices/res/values-hu/strings.xml
index cd11cb4..1c7a89a 100644
--- a/packages/InputDevices/res/values-hu/strings.xml
+++ b/packages/InputDevices/res/values-hu/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"finn"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"horvát"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"cseh"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"QWERTY kiosztás (cseh)"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"észt"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"magyar"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"izlandi"</string>
diff --git a/packages/InputDevices/res/values-hy/strings.xml b/packages/InputDevices/res/values-hy/strings.xml
index 4528377..fff9fbc 100644
--- a/packages/InputDevices/res/values-hy/strings.xml
+++ b/packages/InputDevices/res/values-hy/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Ֆիններեն"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Խորվաթերեն"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Չեխերեն"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"QWERTY – չեխական ոճ"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Էստոներեն"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Հունգարերեն"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Իսլանդերեն"</string>
diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml
index cd14c31..04a6dfa 100644
--- a/packages/InputDevices/res/values-in/strings.xml
+++ b/packages/InputDevices/res/values-in/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandia"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroasia"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Ceko"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Gaya QWERTY Ceko"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonia"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hungaria"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandia"</string>
diff --git a/packages/InputDevices/res/values-is/strings.xml b/packages/InputDevices/res/values-is/strings.xml
index deb1f1c..a60332a 100644
--- a/packages/InputDevices/res/values-is/strings.xml
+++ b/packages/InputDevices/res/values-is/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnskt"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Króatískt"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Tékkneskt"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Tékkneskt QWERTY-útlit"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Eistneskt"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Ungverskt"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Íslenskt"</string>
diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml
index 6c4adb8..ac137e4 100644
--- a/packages/InputDevices/res/values-it/strings.xml
+++ b/packages/InputDevices/res/values-it/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandese"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croato"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Ceco"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Stile QWERTY ceco"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estone"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Ungherese"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandese"</string>
diff --git a/packages/InputDevices/res/values-iw/strings.xml b/packages/InputDevices/res/values-iw/strings.xml
index 3e9ffa8..544dde2 100644
--- a/packages/InputDevices/res/values-iw/strings.xml
+++ b/packages/InputDevices/res/values-iw/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"פינית"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"קרואטית"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"צ\'כית"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"סגנון מקלדת QWERTY בצ\'כית"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"אסטונית"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"הונגרית"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"איסלנדית"</string>
diff --git a/packages/InputDevices/res/values-ja/strings.xml b/packages/InputDevices/res/values-ja/strings.xml
index e60b50c..717cbb9 100644
--- a/packages/InputDevices/res/values-ja/strings.xml
+++ b/packages/InputDevices/res/values-ja/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"フィンランド語"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"クロアチア語"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"チェコ語"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"QWERTY スタイル(チェコ語)"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"エストニア語"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ハンガリー語"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"アイスランド語"</string>
diff --git a/packages/InputDevices/res/values-ka/strings.xml b/packages/InputDevices/res/values-ka/strings.xml
index 3e02764..ee42b35 100644
--- a/packages/InputDevices/res/values-ka/strings.xml
+++ b/packages/InputDevices/res/values-ka/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"ფინური"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"ხორვატიული"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"ჩეხური"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"ჩეხური QWERTY სტილი"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"ესტონური"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"უნგრული"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"ისლანდიური"</string>
diff --git a/packages/InputDevices/res/values-km/strings.xml b/packages/InputDevices/res/values-km/strings.xml
index 8be590e..a2c3262 100644
--- a/packages/InputDevices/res/values-km/strings.xml
+++ b/packages/InputDevices/res/values-km/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"ហ្វាំងឡង់"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"ក្រូអាត"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"ឆេក"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"រចនាប័ទ្ម Czech QWERTY"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"អេស្តូនី"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ហុងគ្រី"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"អ៊ីស្លង់"</string>
diff --git a/packages/InputDevices/res/values-lo/strings.xml b/packages/InputDevices/res/values-lo/strings.xml
index b999498..26e7ad4 100644
--- a/packages/InputDevices/res/values-lo/strings.xml
+++ b/packages/InputDevices/res/values-lo/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"ຟິນນິຊ"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"ໂຄຣເອທຽນ"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"ເຊກ"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"ຮູບແບບ QWERTY ເຊກ"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"ເອສໂຕນຽນ"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ຮັງກາຣຽນ"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"ໄອສແລນດິກ"</string>
diff --git a/packages/InputDevices/res/values-lt/strings.xml b/packages/InputDevices/res/values-lt/strings.xml
index df2e376..d0b855d 100644
--- a/packages/InputDevices/res/values-lt/strings.xml
+++ b/packages/InputDevices/res/values-lt/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Suomių k."</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroatų k."</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Čekų k."</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Čekų QWERTY stilius"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estų k."</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Vengrų k."</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandų k."</string>
diff --git a/packages/InputDevices/res/values-mk/strings.xml b/packages/InputDevices/res/values-mk/strings.xml
index b65db16..ce5e8f2 100644
--- a/packages/InputDevices/res/values-mk/strings.xml
+++ b/packages/InputDevices/res/values-mk/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Фински"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Хрватски"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Чешки"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Чешка QWERTY-тастатура"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Естонски"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Унгарски"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Исландски"</string>
diff --git a/packages/InputDevices/res/values-my/strings.xml b/packages/InputDevices/res/values-my/strings.xml
index 6eaeeea3..57748db 100644
--- a/packages/InputDevices/res/values-my/strings.xml
+++ b/packages/InputDevices/res/values-my/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"ဖင်လန်"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"ခရိုအေးရှန်း"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"ချက်"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"\'ချက် QWERTY\' ပုံစံ"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"အက်စ်စတိုးနီးယန်း"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ဟန်ဂေရီယန်း"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"အိုက်စလန်"</string>
diff --git a/packages/InputDevices/res/values-nb/strings.xml b/packages/InputDevices/res/values-nb/strings.xml
index 9a0ac84..6059b4c 100644
--- a/packages/InputDevices/res/values-nb/strings.xml
+++ b/packages/InputDevices/res/values-nb/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finsk"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroatisk"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Tsjekkisk"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Tsjekkisk QWERTY-stil"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estisk"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Ungarsk"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandsk"</string>
diff --git a/packages/InputDevices/res/values-nl/strings.xml b/packages/InputDevices/res/values-nl/strings.xml
index 578b2aa..9767f92 100644
--- a/packages/InputDevices/res/values-nl/strings.xml
+++ b/packages/InputDevices/res/values-nl/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Fins"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroatisch"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Tsjechisch"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Tsjechisch - QWERTY-stijl"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estlands"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hongaars"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"IJslands"</string>
diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml
index 44f9e3f..c375119 100644
--- a/packages/InputDevices/res/values-pl/strings.xml
+++ b/packages/InputDevices/res/values-pl/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Fiński"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Chorwacki"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Czeski"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Styl czeskiej klawiatury QWERTY"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estoński"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Węgierski"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandzki"</string>
diff --git a/packages/InputDevices/res/values-pt-rBR/strings.xml b/packages/InputDevices/res/values-pt-rBR/strings.xml
index d282f2c..d6687c8 100644
--- a/packages/InputDevices/res/values-pt-rBR/strings.xml
+++ b/packages/InputDevices/res/values-pt-rBR/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandês"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croata"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Tcheco"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Estilo QWERTY tcheco"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estoniano"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Húngaro"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandês"</string>
diff --git a/packages/InputDevices/res/values-pt-rPT/strings.xml b/packages/InputDevices/res/values-pt-rPT/strings.xml
index 052e2ad..4f8a432 100644
--- a/packages/InputDevices/res/values-pt-rPT/strings.xml
+++ b/packages/InputDevices/res/values-pt-rPT/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandês"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croata"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Checo"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Estilo QWERTY checo"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estónio"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Húngaro"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandês"</string>
diff --git a/packages/InputDevices/res/values-pt/strings.xml b/packages/InputDevices/res/values-pt/strings.xml
index d282f2c..d6687c8 100644
--- a/packages/InputDevices/res/values-pt/strings.xml
+++ b/packages/InputDevices/res/values-pt/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandês"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croata"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Tcheco"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Estilo QWERTY tcheco"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estoniano"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Húngaro"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandês"</string>
diff --git a/packages/InputDevices/res/values-ro/strings.xml b/packages/InputDevices/res/values-ro/strings.xml
index 7d85df0..1b38bdd 100644
--- a/packages/InputDevices/res/values-ro/strings.xml
+++ b/packages/InputDevices/res/values-ro/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandeză"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croată"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Cehă"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Stil QWERTY cehă"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estoniană"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Maghiară"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandeză"</string>
diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml
index a1d3185..501517d 100644
--- a/packages/InputDevices/res/values-ru/strings.xml
+++ b/packages/InputDevices/res/values-ru/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"финский"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"хорватский"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"чешский"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Чешский (QWERTY)"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"эстонский"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"венгерский"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"исландский"</string>
diff --git a/packages/InputDevices/res/values-si/strings.xml b/packages/InputDevices/res/values-si/strings.xml
index 49b9907..c136616 100644
--- a/packages/InputDevices/res/values-si/strings.xml
+++ b/packages/InputDevices/res/values-si/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"ෆින්ලන්ත"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"ක්රොඒෂියානු"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"චෙක්"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"චෙක් QWERTY විලාසය"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"එස්තෝනියානු"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"හංගේරියානු"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"අයිස්ලන්ත"</string>
diff --git a/packages/InputDevices/res/values-sl/strings.xml b/packages/InputDevices/res/values-sl/strings.xml
index 613dda0..91ba472 100644
--- a/packages/InputDevices/res/values-sl/strings.xml
+++ b/packages/InputDevices/res/values-sl/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"finska"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"hrvaška"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"češka"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Razpored QWERTY za češčino"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"estonska"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"madžarska"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"islandska"</string>
diff --git a/packages/InputDevices/res/values-sq/strings.xml b/packages/InputDevices/res/values-sq/strings.xml
index 29e2278..3128025 100644
--- a/packages/InputDevices/res/values-sq/strings.xml
+++ b/packages/InputDevices/res/values-sq/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"finlandisht"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"kroatisht"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"çekisht"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Tastiera QWERTY çekisht"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"estonisht"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"hungarisht"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"islandisht"</string>
diff --git a/packages/InputDevices/res/values-sv/strings.xml b/packages/InputDevices/res/values-sv/strings.xml
index 7b70767..a4c0fca 100644
--- a/packages/InputDevices/res/values-sv/strings.xml
+++ b/packages/InputDevices/res/values-sv/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finskt"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroatiskt"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Tjeckiskt"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Tjeckiskt QWERTY"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estniskt"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Ungerskt"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Isländskt"</string>
diff --git a/packages/InputDevices/res/values-sw/strings.xml b/packages/InputDevices/res/values-sw/strings.xml
index d82959a..1e5c8d7 100644
--- a/packages/InputDevices/res/values-sw/strings.xml
+++ b/packages/InputDevices/res/values-sw/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Kifinlandi"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kikroeshia"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Kicheki"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Muundo wa QWERTY wa Kicheki"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Kiestonia"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Kihungari"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Kiaislandi"</string>
diff --git a/packages/InputDevices/res/values-te/strings.xml b/packages/InputDevices/res/values-te/strings.xml
index e07b2b5..b6caafd 100644
--- a/packages/InputDevices/res/values-te/strings.xml
+++ b/packages/InputDevices/res/values-te/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"ఫిన్నిష్"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"క్రొయేషియన్"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"చెక్"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"చెక్ QWERTY స్టయిల్"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"ఎస్టోనియన్"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"హంగేరియన్"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"ఐస్లాండిక్"</string>
diff --git a/packages/InputDevices/res/values-th/strings.xml b/packages/InputDevices/res/values-th/strings.xml
index 926c127..1170f86 100644
--- a/packages/InputDevices/res/values-th/strings.xml
+++ b/packages/InputDevices/res/values-th/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"ฟินแลนด์"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"โครเอเชีย"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"เช็ก"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"แบบ Czech QWERTY"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"เอสโตเนีย"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ฮังการี"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"ไอซ์แลนดิก"</string>
diff --git a/packages/InputDevices/res/values-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml
index 41d6a18..97b878b 100644
--- a/packages/InputDevices/res/values-tl/strings.xml
+++ b/packages/InputDevices/res/values-tl/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finnish"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Croatian"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Czech"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Czech QWERTY style"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonian"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hungarian"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Icelandic"</string>
diff --git a/packages/InputDevices/res/values-tr/strings.xml b/packages/InputDevices/res/values-tr/strings.xml
index 9249930..7e9b5e5 100644
--- a/packages/InputDevices/res/values-tr/strings.xml
+++ b/packages/InputDevices/res/values-tr/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Fince"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Hırvatça"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Çekçe"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Çekçe QWERTY stili"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonca"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Macarca"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"İzlandaca"</string>
diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml
index 20f6046..db5de4c 100644
--- a/packages/InputDevices/res/values-uk/strings.xml
+++ b/packages/InputDevices/res/values-uk/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"фінська"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"хорватська"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"чеська"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Чеська (QWERTY)"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"естонська"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"угорська"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"ісландська"</string>
diff --git a/packages/InputDevices/res/values-ur/strings.xml b/packages/InputDevices/res/values-ur/strings.xml
index 7e47430..bf9e5ad 100644
--- a/packages/InputDevices/res/values-ur/strings.xml
+++ b/packages/InputDevices/res/values-ur/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"فنش"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"کروشیائی"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"چیک"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Czech QWERTY طرز"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"اسٹونیائی"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ہنگریائی"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"آئس لینڈک"</string>
diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml
index 62e5952..eabaa19 100644
--- a/packages/InputDevices/res/values-vi/strings.xml
+++ b/packages/InputDevices/res/values-vi/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Tiếng Phần Lan"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Tiếng Croatia"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Tiếng Séc"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Kiểu QWERTY tiếng Séc"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Tiếng Estonia"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Tiếng Hungary"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Tiếng Ai-xơ-len"</string>
diff --git a/packages/InputDevices/res/values-zh-rCN/strings.xml b/packages/InputDevices/res/values-zh-rCN/strings.xml
index 0b6ec0f..3eb4b2c 100644
--- a/packages/InputDevices/res/values-zh-rCN/strings.xml
+++ b/packages/InputDevices/res/values-zh-rCN/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"芬兰语"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"克罗地亚语"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"捷克语"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"捷克语 QWERTY 样式"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"爱沙尼亚语"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"匈牙利语"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"冰岛语"</string>
diff --git a/packages/InputDevices/res/values-zh-rHK/strings.xml b/packages/InputDevices/res/values-zh-rHK/strings.xml
index 22b46ca..37cd533 100644
--- a/packages/InputDevices/res/values-zh-rHK/strings.xml
+++ b/packages/InputDevices/res/values-zh-rHK/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"芬蘭文"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"克羅地亞文"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"捷克文"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"捷克文 QWERTY 樣式"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"愛沙尼亞文"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"匈牙利文"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"冰島文"</string>
diff --git a/packages/InputDevices/res/values-zh-rTW/strings.xml b/packages/InputDevices/res/values-zh-rTW/strings.xml
index abe16f3..8d2de40 100644
--- a/packages/InputDevices/res/values-zh-rTW/strings.xml
+++ b/packages/InputDevices/res/values-zh-rTW/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"芬蘭文"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"克羅埃西亞文"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"捷克文"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"捷克文 QWERTY 鍵盤"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"愛沙尼亞文"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"匈牙利文"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"冰島文"</string>
diff --git a/packages/InputDevices/res/values-zu/strings.xml b/packages/InputDevices/res/values-zu/strings.xml
index c942251..ddb688a 100644
--- a/packages/InputDevices/res/values-zu/strings.xml
+++ b/packages/InputDevices/res/values-zu/strings.xml
@@ -26,8 +26,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Isi-Finnish"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Isi-Croatian"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Isi-Czech"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Isitayela se-Czech QWERTY"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Isi-Estonian"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Isi-Hungarian"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Isi-Icelandic"</string>
diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml
index e95a159..9d068fe 100644
--- a/packages/InputDevices/res/values/strings.xml
+++ b/packages/InputDevices/res/values/strings.xml
@@ -54,6 +54,9 @@
<!-- Bulgarian keyboard layout label. [CHAR LIMIT=35] -->
<string name="keyboard_layout_bulgarian">Bulgarian</string>
+ <!-- Bulgarian keyboard layout label. [CHAR LIMIT=35] -->
+ <string name="keyboard_layout_bulgarian_phonetic">Bulgarian, Phonetic</string>
+
<!-- Italian keyboard layout label. [CHAR LIMIT=35] -->
<string name="keyboard_layout_italian">Italian</string>
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index aa599ae..6b78b68 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -64,6 +64,10 @@
android:label="@string/keyboard_layout_bulgarian"
android:keyboardLayout="@raw/keyboard_layout_bulgarian" />
+ <keyboard-layout android:name="keyboard_layout_bulgarian_phonetic"
+ android:label="@string/keyboard_layout_bulgarian_phonetic"
+ android:keyboardLayout="@raw/keyboard_layout_bulgarian_phonetic" />
+
<keyboard-layout android:name="keyboard_layout_italian"
android:label="@string/keyboard_layout_italian"
android:keyboardLayout="@raw/keyboard_layout_italian" />
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
index 467a60e..8e140ca 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
@@ -31,6 +31,7 @@
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.SystemClock;
import android.print.PrintManager;
@@ -250,9 +251,13 @@
Log.i(LOG_TAG, "onStartLoading() " + FusedPrintersProvider.this.hashCode());
}
- mLocationManager.requestLocationUpdates(LocationRequest.create()
- .setQuality(LocationRequest.POWER_LOW).setInterval(LOCATION_UPDATE_MS), this,
- Looper.getMainLooper());
+ mLocationManager.requestLocationUpdates(
+ LocationManager.FUSED_PROVIDER,
+ new LocationRequest.Builder(LOCATION_UPDATE_MS)
+ .setQuality(LocationRequest.POWER_LOW)
+ .build(),
+ new HandlerExecutor(new Handler(Looper.getMainLooper())),
+ this);
Location lastLocation = mLocationManager.getLastLocation();
if (lastLocation != null) {
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 9acfa0d..9ca1814 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -25,7 +25,7 @@
<string name="wifi_remembered" msgid="3266709779723179188">"تم الحفظ"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"غير متصلة"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"غير مفعّلة"</string>
- <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"تعذّرت تهيئة عنوان IP"</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">"تعذّر اتصال WiFi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"حدثت مشكلة في المصادقة"</string>
@@ -292,8 +292,8 @@
<string name="dev_logpersist_clear_warning_message" msgid="6447590867594287413">"عندما نتوقف عن رصد أي أخطاء باستخدام المسجِّل الدائم مرة أخرى، يتعين علينا محو بيانات المسجِّل الموجودة على جهازك."</string>
<string name="select_logpersist_title" msgid="447071974007104196">"تخزين بيانات المسجِّل باستمرار على الجهاز"</string>
<string name="select_logpersist_dialog_title" msgid="7745193591195485594">"تحديد مخازن السجلات المؤقتة المراد تخزينها باستمرار على الجهاز"</string>
- <string name="select_usb_configuration_title" msgid="6339801314922294586">"حدد تهيئة USB"</string>
- <string name="select_usb_configuration_dialog_title" msgid="3579567144722589237">"حدد تهيئة USB"</string>
+ <string name="select_usb_configuration_title" msgid="6339801314922294586">"حدد إعداد USB"</string>
+ <string name="select_usb_configuration_dialog_title" msgid="3579567144722589237">"حدد إعداد USB"</string>
<string name="allow_mock_location" msgid="2102650981552527884">"السماح بمواقع وهمية"</string>
<string name="allow_mock_location_summary" msgid="179780881081354579">"السماح بمواقع وهمية"</string>
<string name="debug_view_attributes" msgid="3539609843984208216">"تفعيل فحص سمة العرض"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 126f9b9..41d6afc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -46,6 +46,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
/**
* MediaDevice represents a media device(such like Bluetooth device, cast device and phone device).
@@ -354,6 +355,13 @@
}
/**
+ * Gets the supported features of the route.
+ */
+ public List<String> getFeatures() {
+ return mRouteInfo.getFeatures();
+ }
+
+ /**
* Check if it is CarKit device
* @return true if it is CarKit device
*/
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 9c92b46..3ccb1f4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -22,6 +22,8 @@
import static android.os.Process.SYSTEM_UID;
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY;
import static com.android.providers.settings.SettingsState.FALLBACK_FILE_SUFFIX;
@@ -41,6 +43,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.om.IOverlayManager;
+import android.content.om.OverlayInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
@@ -3414,7 +3417,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 191;
+ private static final int SETTINGS_VERSION = 192;
private final int mUserId;
@@ -4783,6 +4786,20 @@
currentVersion = 191;
}
+ if (currentVersion == 191) {
+ final SettingsState secureSettings = getSecureSettingsLocked(userId);
+ int mode = getContext().getResources().getInteger(
+ com.android.internal.R.integer.config_navBarInteractionMode);
+ if (mode == NAV_BAR_MODE_GESTURAL) {
+ switchToDefaultGestureNavBackInset(userId, secureSettings);
+ }
+ migrateBackGestureSensitivity(Secure.BACK_GESTURE_INSET_SCALE_LEFT, userId,
+ secureSettings);
+ migrateBackGestureSensitivity(Secure.BACK_GESTURE_INSET_SCALE_RIGHT, userId,
+ secureSettings);
+ currentVersion = 192;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
@@ -4801,6 +4818,83 @@
}
}
+ /**
+ * Previously, We were using separate overlay packages for different back inset sizes. Now,
+ * we have a single overlay package for gesture navigation mode, and set the inset size via
+ * a secure.settings field.
+ *
+ * If a non-default overlay package is enabled, then enable the default overlay exclusively,
+ * and set the calculated inset size difference as a scale value in secure.settings.
+ */
+ private void switchToDefaultGestureNavBackInset(int userId, SettingsState secureSettings) {
+ try {
+ final IOverlayManager om = IOverlayManager.Stub.asInterface(
+ ServiceManager.getService(Context.OVERLAY_SERVICE));
+ final OverlayInfo info = om.getOverlayInfo(NAV_BAR_MODE_GESTURAL_OVERLAY, userId);
+ if (info != null && !info.isEnabled()) {
+ final int curInset = getContext().getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.config_backGestureInset);
+ om.setEnabledExclusiveInCategory(NAV_BAR_MODE_GESTURAL_OVERLAY, userId);
+ final int defInset = getContext().getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.config_backGestureInset);
+
+ final float scale = defInset == 0 ? 1.0f : ((float) curInset) / defInset;
+ if (scale != 1.0f) {
+ secureSettings.insertSettingLocked(
+ Secure.BACK_GESTURE_INSET_SCALE_LEFT,
+ Float.toString(scale), null /* tag */, false /* makeDefault */,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ secureSettings.insertSettingLocked(
+ Secure.BACK_GESTURE_INSET_SCALE_RIGHT,
+ Float.toString(scale), null /* tag */, false /* makeDefault */,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Moved back sensitivity for user " + userId
+ + " to scale " + scale);
+ }
+ }
+ }
+ } catch (SecurityException | IllegalStateException | RemoteException e) {
+ Slog.e(LOG_TAG, "Failed to switch to default gesture nav overlay for user "
+ + userId);
+ }
+ }
+
+ private void migrateBackGestureSensitivity(String side, int userId,
+ SettingsState secureSettings) {
+ final Setting currentScale = secureSettings.getSettingLocked(side);
+ if (currentScale.isNull()) {
+ return;
+ }
+ float current = 1.0f;
+ try {
+ current = Float.parseFloat(currentScale.getValue());
+ } catch (NumberFormatException e) {
+ // Do nothing. Overwrite with default value.
+ }
+
+ // Inset scale migration across all devices
+ // Old(24dp): 0.66 0.75 0.83 1.00 1.08 1.33 1.66
+ // New(30dp): 0.60 0.60 1.00 1.00 1.00 1.00 1.33
+ final float low = 0.76f; // Values smaller than this will map to 0.6
+ final float high = 1.65f; // Values larger than this will map to 1.33
+ float newScale;
+ if (current < low) {
+ newScale = 0.6f;
+ } else if (current < high) {
+ newScale = 1.0f;
+ } else {
+ newScale = 1.33f;
+ }
+ secureSettings.insertSettingLocked(side, Float.toString(newScale),
+ null /* tag */, false /* makeDefault */,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Changed back sensitivity from " + current + " to " + newScale
+ + " for user " + userId + " on " + side);
+ }
+ }
+
private void ensureLegacyDefaultValueAndSystemSetUpdatedLocked(SettingsState settings,
int userId) {
List<String> names = settings.getSettingNamesLocked();
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 06a97b1..a1b9dcd 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -772,5 +772,12 @@
</intent-filter>
</receiver>
+ <receiver android:name=".media.dialog.MediaOutputDialogReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG" />
+ </intent-filter>
+ </receiver>
+
</application>
</manifest>
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index e246917..26cead2 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -86,6 +86,10 @@
*/
InstanceId getInstanceId();
+ default boolean isTileReady() {
+ return false;
+ }
+
@ProvidesInterface(version = Callback.VERSION)
public interface Callback {
public static final int VERSION = 1;
diff --git a/packages/SystemUI/res/drawable/ic_move.xml b/packages/SystemUI/res/drawable/ic_move_magnification.xml
similarity index 97%
rename from packages/SystemUI/res/drawable/ic_move.xml
rename to packages/SystemUI/res/drawable/ic_move_magnification.xml
index e82c9d0..ed97d0cc 100644
--- a/packages/SystemUI/res/drawable/ic_move.xml
+++ b/packages/SystemUI/res/drawable/ic_move_magnification.xml
@@ -16,7 +16,7 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
- <shape android:shape="oval">
+ <shape android:shape="rectangle">
<solid
android:color="@android:color/black" />
<size
diff --git a/packages/SystemUI/res/drawable/media_output_dialog_background.xml b/packages/SystemUI/res/drawable/media_output_dialog_background.xml
new file mode 100644
index 0000000..3ceb0f6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/media_output_dialog_background.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+ <shape android:shape="rectangle">
+ <corners android:radius="8dp" />
+ <solid android:color="?android:attr/colorBackground" />
+ </shape>
+</inset>
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
new file mode 100644
index 0000000..0229e6e
--- /dev/null
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/media_output_dialog"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="94dp"
+ android:gravity="start|center_vertical"
+ android:paddingStart="16dp"
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+id/header_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingEnd="16dp"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="16dp"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/header_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:textSize="20sp"/>
+
+ <TextView
+ android:id="@+id/header_subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:fontFamily="roboto-regular"
+ android:textSize="14sp"/>
+
+ </LinearLayout>
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:attr/listDivider"/>
+
+ <LinearLayout
+ android:id="@+id/device_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:orientation="vertical">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="12dp"/>
+
+ <include
+ layout="@layout/media_output_list_item"
+ android:id="@+id/group_item_controller"
+ android:visibility="gone"/>
+
+ <View
+ android:id="@+id/group_item_divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:attr/listDivider"
+ android:visibility="gone"/>
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/list_result"
+ android:scrollbars="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:overScrollMode="never"/>
+
+ <View
+ android:id="@+id/list_bottom_padding"
+ android:layout_width="match_parent"
+ android:layout_height="12dp"/>
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:attr/listDivider"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/stop"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="64dp"
+ android:text="@string/keyboard_key_media_stop"
+ android:visibility="gone"/>
+
+ <Space
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+
+ <Button
+ android:id="@+id/done"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="64dp"
+ android:layout_marginEnd="0dp"
+ android:text="@string/inline_done_button"/>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml
new file mode 100644
index 0000000..92d0858
--- /dev/null
+++ b/packages/SystemUI/res/layout/media_output_list_item.xml
@@ -0,0 +1,112 @@
+<?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
+ android:id="@+id/device_container"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="64dp">
+
+ <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>
+
+ <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"/>
+
+ <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"/>
+ <ProgressBar
+ android:id="@+id/volume_indeterminate_progress"
+ style="@*android:style/Widget.Material.ProgressBar.Horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="15dp"
+ android:layout_marginBottom="1dp"
+ android:layout_alignParentBottom="true"
+ android:indeterminate="true"
+ android:indeterminateOnly="true"
+ android:visibility="gone"/>
+ <SeekBar
+ android:id="@+id/volume_seekbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"/>
+ </RelativeLayout>
+
+ <View
+ 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/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
diff --git a/packages/SystemUI/res/layout/window_magnifier_view.xml b/packages/SystemUI/res/layout/window_magnifier_view.xml
index 25d63e3..6ced978 100644
--- a/packages/SystemUI/res/layout/window_magnifier_view.xml
+++ b/packages/SystemUI/res/layout/window_magnifier_view.xml
@@ -23,13 +23,13 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/magnification_outer_border_margin"
- android:background="@android:color/black" />
+ android:background="@android:color/black"/>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/magnification_inner_border_margin"
- android:background="@color/magnification_border_color" />
+ android:background="@color/magnification_border_color"/>
<RelativeLayout
android:layout_width="match_parent"
@@ -40,32 +40,32 @@
android:id="@+id/left_handle"
android:layout_width="@dimen/magnification_border_drag_size"
android:layout_height="match_parent"
- android:layout_above="@+id/bottom_handle" />
+ android:layout_above="@+id/bottom_handle"/>
<View
android:id="@+id/top_handle"
android:layout_width="match_parent"
android:layout_height="@dimen/magnification_border_drag_size"
- android:layout_alignParentTop="true" />
+ android:layout_alignParentTop="true"/>
<View
android:id="@+id/right_handle"
android:layout_width="@dimen/magnification_border_drag_size"
android:layout_height="match_parent"
android:layout_above="@+id/bottom_handle"
- android:layout_alignParentEnd="true" />
+ android:layout_alignParentEnd="true"/>
<View
android:id="@+id/bottom_handle"
android:layout_width="match_parent"
android:layout_height="@dimen/magnification_border_drag_size"
- android:layout_alignParentBottom="true" />
+ android:layout_alignParentBottom="true"/>
<SurfaceView
android:id="@+id/surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_margin="@dimen/magnification_mirror_surface_margin" />
+ android:layout_margin="@dimen/magnification_mirror_surface_margin"/>
</RelativeLayout>
@@ -73,8 +73,9 @@
android:id="@+id/drag_handle"
android:layout_width="@dimen/magnification_drag_view_size"
android:layout_height="@dimen/magnification_drag_view_size"
+ android:layout_margin="@dimen/magnification_outer_border_margin"
android:layout_gravity="right|bottom"
android:scaleType="center"
- android:src="@drawable/ic_move" />
+ android:src="@drawable/ic_move_magnification"/>
</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index 6630401..981a953 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -29,6 +29,7 @@
<item>com.android.systemui.util.NotificationChannels</item>
<item>com.android.systemui.volume.VolumeUI</item>
<item>com.android.systemui.statusbar.tv.TvStatusBar</item>
+ <item>com.android.systemui.statusbar.tv.TvNotificationPanel</item>
<item>com.android.systemui.usb.StorageNotification</item>
<item>com.android.systemui.power.PowerUI</item>
<item>com.android.systemui.media.RingtonePlayer</item>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 875fe14..98e8cde 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1366,4 +1366,11 @@
<dimen name="config_rounded_mask_size">@*android:dimen/rounded_corner_radius</dimen>
<dimen name="config_rounded_mask_size_top">@*android:dimen/rounded_corner_radius_top</dimen>
<dimen name="config_rounded_mask_size_bottom">@*android:dimen/rounded_corner_radius_bottom</dimen>
+
+ <!-- Output switcher panel related dimensions -->
+ <dimen name="media_output_dialog_padding_top">11dp</dimen>
+ <dimen name="media_output_dialog_list_max_height">364dp</dimen>
+ <dimen name="media_output_dialog_header_album_icon_size">52dp</dimen>
+ <dimen name="media_output_dialog_header_back_icon_size">36dp</dimen>
+ <dimen name="media_output_dialog_icon_corner_radius">16dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 2f018b9..d815681 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -170,6 +170,9 @@
<item type="id" name="accessibility_action_controls_move_before" />
<item type="id" name="accessibility_action_controls_move_after" />
+ <item type="id" name="accessibility_action_qs_move_to_position" />
+ <item type="id" name="accessibility_action_qs_add_to_position" />
+
<!-- Accessibility actions for PIP -->
<item type="id" name="action_pip_resize" />
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e577b96..f693149 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2260,23 +2260,26 @@
<!-- SysUI Tuner: Other section -->
<string name="other">Other</string>
- <!-- Accessibility description of a QS tile while editing positions [CHAR LIMIT=NONE] -->
- <string name="accessibility_qs_edit_tile_label">Position <xliff:g id="position" example="2">%1$d</xliff:g>, <xliff:g id="tile_name" example="Wi-Fi">%2$s</xliff:g>. Double tap to edit.</string>
+ <!-- Accessibility description of action to remove QS tile on click. It will read as "Double-tap to remove tile" in screen readers [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_remove_tile_action">remove tile</string>
- <!-- Accessibility description of a QS tile while editing positions [CHAR LIMIT=NONE] -->
- <string name="accessibility_qs_edit_add_tile_label"><xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g>. Double tap to add.</string>
+ <!-- Accessibility action of action to add QS tile to end. It will read as "Double-tap to add tile to end" in screen readers [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_tile_add_action">add tile to end</string>
- <!-- Accessibility description of option to move QS tile [CHAR LIMIT=NONE] -->
- <string name="accessibility_qs_edit_move_tile">Move <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g></string>
+ <!-- Accessibility action for context menu to move QS tile [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_tile_start_move">Move tile</string>
- <!-- Accessibility description of option to remove QS tile [CHAR LIMIT=NONE] -->
- <string name="accessibility_qs_edit_remove_tile">Remove <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g></string>
+ <!-- Accessibility action for context menu to add QS tile [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_tile_start_add">Add tile</string>
- <!-- Accessibility action when QS tile is to be added [CHAR LIMIT=NONE] -->
- <string name="accessibility_qs_edit_tile_add">Add <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g> to position <xliff:g id="position" example="5">%2$d</xliff:g></string>
+ <!-- Accessibility description when QS tile is to be moved, indicating the destination position [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_tile_move_to_position">Move to <xliff:g id="position" example="5">%1$d</xliff:g></string>
- <!-- Accessibility action when QS tile is to be moved [CHAR LIMIT=NONE] -->
- <string name="accessibility_qs_edit_tile_move">Move <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g> to position <xliff:g id="position" example="5">%2$d</xliff:g></string>
+ <!-- Accessibility description when QS tile is to be added, indicating the destination position [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_tile_add_to_position">Add to position <xliff:g id="position" example="5">%1$d</xliff:g></string>
+
+ <!-- Accessibility description indicating the currently selected tile's position. Only used for tiles that are currently in use [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_position">Position <xliff:g id="position" example="5">%1$d</xliff:g></string>
<!-- Accessibility label for window when QS editing is happening [CHAR LIMIT=NONE] -->
<string name="accessibility_desc_quick_settings_edit">Quick settings editor.</string>
@@ -2799,4 +2802,19 @@
<string name="udfps_hbm_enable_command" translatable="false"></string>
<!-- Device-specific payload for disabling the high-brightness mode -->
<string name="udfps_hbm_disable_command" translatable="false"></string>
+
+ <!-- Title for the media output group dialog with media related devices [CHAR LIMIT=50] -->
+ <string name="media_output_dialog_add_output">Add outputs</string>
+ <!-- Title for the media output slice with group devices [CHAR LIMIT=50] -->
+ <string name="media_output_dialog_group">Group</string>
+ <!-- Summary for media output group with only one device which is active [CHAR LIMIT=NONE] -->
+ <string name="media_output_dialog_single_device">1 device selected</string>
+ <!-- Summary for media output group with the active device count [CHAR LIMIT=NONE] -->
+ <string name="media_output_dialog_multiple_devices"><xliff:g id="count" example="2">%1$d</xliff:g> devices selected</string>
+ <!-- Summary for disconnected status [CHAR LIMIT=50] -->
+ <string name="media_output_dialog_disconnected"><xliff:g id="device_name" example="My device">%1$s</xliff:g> (disconnected)</string>
+ <!-- Summary for connecting error message [CHAR LIMIT=NONE] -->
+ <string name="media_output_dialog_connect_failed">Couldn\'t connect. Try again.</string>
+ <!-- Title for pairing item [CHAR LIMIT=60] -->
+ <string name="media_output_dialog_pairing_new">Pair new device</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 58563f4..2b0a963 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -388,6 +388,10 @@
<item name="android:windowIsFloating">true</item>
</style>
+ <style name="Theme.SystemUI.Dialog.MediaOutput">
+ <item name="android:windowBackground">@drawable/media_output_dialog_background</item>
+ </style>
+
<style name="QSBorderlessButton">
<item name="android:padding">12dp</item>
<item name="android:background">@drawable/qs_btn_borderless_rect</item>
@@ -735,5 +739,4 @@
* Title: headline, medium 20sp
* Message: body, 16 sp -->
<style name="Theme.ControlsRequestDialog" parent="@*android:style/Theme.DeviceDefault.Dialog.Alert"/>
-
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
index e99245f..23195af 100644
--- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
@@ -33,9 +33,13 @@
import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.dagger.KeyguardBouncerScope;
+import com.android.systemui.dagger.qualifiers.Main;
import java.util.NoSuchElementException;
+import javax.inject.Inject;
+
/**
* Encapsulates all logic for secondary lockscreen state management.
*/
@@ -142,9 +146,9 @@
}
};
- public AdminSecondaryLockScreenController(Context context, ViewGroup parent,
+ private AdminSecondaryLockScreenController(Context context, KeyguardSecurityContainer parent,
KeyguardUpdateMonitor updateMonitor, KeyguardSecurityCallback callback,
- Handler handler) {
+ @Main Handler handler) {
mContext = context;
mHandler = handler;
mParent = parent;
@@ -234,4 +238,26 @@
getHolder().removeCallback(mSurfaceHolderCallback);
}
}
+
+ @KeyguardBouncerScope
+ public static class Factory {
+ private final Context mContext;
+ private final KeyguardSecurityContainer mParent;
+ private final KeyguardUpdateMonitor mUpdateMonitor;
+ private final Handler mHandler;
+
+ @Inject
+ public Factory(Context context, KeyguardSecurityContainer parent,
+ KeyguardUpdateMonitor updateMonitor, @Main Handler handler) {
+ mContext = context;
+ mParent = parent;
+ mUpdateMonitor = updateMonitor;
+ mHandler = handler;
+ }
+
+ public AdminSecondaryLockScreenController create(KeyguardSecurityCallback callback) {
+ return new AdminSecondaryLockScreenController(mContext, mParent, mUpdateMonitor,
+ callback, mHandler);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index 88f4176..cc6df45 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -16,46 +16,26 @@
package com.android.keyguard;
-import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
-import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
-
import android.content.Context;
-import android.content.res.ColorStateList;
-import android.os.AsyncTask;
-import android.os.CountDownTimer;
-import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.View;
-import android.widget.LinearLayout;
-import com.android.internal.util.LatencyTracker;
-import com.android.internal.widget.LockPatternChecker;
-import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
/**
* Base class for PIN and password unlock screens.
*/
-public abstract class KeyguardAbsKeyInputView extends LinearLayout
- implements KeyguardSecurityView, EmergencyButton.EmergencyButtonCallback {
- protected KeyguardSecurityCallback mCallback;
- protected LockPatternUtils mLockPatternUtils;
- protected AsyncTask<?, ?, ?> mPendingLockCheck;
- protected SecurityMessageDisplay mSecurityMessageDisplay;
+public abstract class KeyguardAbsKeyInputView extends KeyguardInputView {
protected View mEcaView;
protected boolean mEnableHaptics;
- private boolean mDismissing;
- protected boolean mResumed;
- private CountDownTimer mCountdownTimer = null;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
// To avoid accidental lockout due to events while the device in in the pocket, ignore
// any passwords with length less than or equal to this length.
protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
+ private KeyDownListener mKeyDownListener;
public KeyguardAbsKeyInputView(Context context) {
this(context, null);
@@ -63,38 +43,10 @@
public KeyguardAbsKeyInputView(Context context, AttributeSet attrs) {
super(context, attrs);
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
}
- @Override
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- mCallback = callback;
- }
-
- @Override
- public void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
- mEnableHaptics = mLockPatternUtils.isTactileFeedbackEnabled();
- }
-
- @Override
- public void reset() {
- // start fresh
- mDismissing = false;
- resetPasswordText(false /* animate */, false /* announce */);
- // if the user is currently locked out, enforce it.
- long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
- KeyguardUpdateMonitor.getCurrentUser());
- if (shouldLockout(deadline)) {
- handleAttemptLockout(deadline);
- } else {
- resetState();
- }
- }
-
- // Allow subclasses to override this behavior
- protected boolean shouldLockout(long deadline) {
- return deadline != 0;
+ void setEnableHaptics(boolean enableHaptics) {
+ mEnableHaptics = enableHaptics;
}
protected abstract int getPasswordTextViewId();
@@ -102,24 +54,7 @@
@Override
protected void onFinishInflate() {
- mLockPatternUtils = new LockPatternUtils(mContext);
mEcaView = findViewById(R.id.keyguard_selector_fade_container);
-
- EmergencyButton button = findViewById(R.id.emergency_call_button);
- if (button != null) {
- button.setCallback(this);
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mSecurityMessageDisplay = KeyguardMessageArea.findSecurityMessageDisplay(this);
- }
-
- @Override
- public void onEmergencyButtonClickedWhenInCall() {
- mCallback.reset();
}
/*
@@ -131,195 +66,14 @@
return R.string.kg_wrong_password;
}
- protected void verifyPasswordAndUnlock() {
- if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
-
- final LockscreenCredential password = getEnteredCredential();
- setPasswordEntryInputEnabled(false);
- if (mPendingLockCheck != null) {
- mPendingLockCheck.cancel(false);
- }
-
- final int userId = KeyguardUpdateMonitor.getCurrentUser();
- if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
- // to avoid accidental lockout, only count attempts that are long enough to be a
- // real password. This may require some tweaking.
- setPasswordEntryInputEnabled(true);
- onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
- password.zeroize();
- return;
- }
-
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
- LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
- }
-
- mKeyguardUpdateMonitor.setCredentialAttempted();
- mPendingLockCheck = LockPatternChecker.checkCredential(
- mLockPatternUtils,
- password,
- userId,
- new LockPatternChecker.OnCheckCallback() {
-
- @Override
- public void onEarlyMatched() {
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(
- ACTION_CHECK_CREDENTIAL);
- }
- onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */,
- true /* isValidPassword */);
- password.zeroize();
- }
-
- @Override
- public void onChecked(boolean matched, int timeoutMs) {
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(
- ACTION_CHECK_CREDENTIAL_UNLOCKED);
- }
- setPasswordEntryInputEnabled(true);
- mPendingLockCheck = null;
- if (!matched) {
- onPasswordChecked(userId, false /* matched */, timeoutMs,
- true /* isValidPassword */);
- }
- password.zeroize();
- }
-
- @Override
- public void onCancelled() {
- // We already got dismissed with the early matched callback, so we cancelled
- // the check. However, we still need to note down the latency.
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(
- ACTION_CHECK_CREDENTIAL_UNLOCKED);
- }
- password.zeroize();
- }
- });
- }
-
- private void onPasswordChecked(int userId, boolean matched, int timeoutMs,
- boolean isValidPassword) {
- boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
- if (matched) {
- mCallback.reportUnlockAttempt(userId, true, 0);
- if (dismissKeyguard) {
- mDismissing = true;
- mCallback.dismiss(true, userId);
- }
- } else {
- if (isValidPassword) {
- mCallback.reportUnlockAttempt(userId, false, timeoutMs);
- if (timeoutMs > 0) {
- long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
- userId, timeoutMs);
- handleAttemptLockout(deadline);
- }
- }
- if (timeoutMs == 0) {
- mSecurityMessageDisplay.setMessage(getWrongPasswordStringId());
- }
- }
- resetPasswordText(true /* animate */, !matched /* announce deletion if no match */);
- }
-
protected abstract void resetPasswordText(boolean animate, boolean announce);
protected abstract LockscreenCredential getEnteredCredential();
protected abstract void setPasswordEntryEnabled(boolean enabled);
protected abstract void setPasswordEntryInputEnabled(boolean enabled);
- // Prevent user from using the PIN/Password entry until scheduled deadline.
- protected void handleAttemptLockout(long elapsedRealtimeDeadline) {
- setPasswordEntryEnabled(false);
- long elapsedRealtime = SystemClock.elapsedRealtime();
- long secondsInFuture = (long) Math.ceil(
- (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
- mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {
-
- @Override
- public void onTick(long millisUntilFinished) {
- int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
- mSecurityMessageDisplay.setMessage(mContext.getResources().getQuantityString(
- R.plurals.kg_too_many_failed_attempts_countdown,
- secondsRemaining, secondsRemaining));
- }
-
- @Override
- public void onFinish() {
- mSecurityMessageDisplay.setMessage("");
- resetState();
- }
- }.start();
- }
-
- protected void onUserInput() {
- if (mCallback != null) {
- mCallback.userActivity();
- mCallback.onUserInput();
- }
- mSecurityMessageDisplay.setMessage("");
- }
-
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
- // Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN.
- // We don't want to consider it valid user input because the UI
- // will already respond to the event.
- if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
- onUserInput();
- }
- return false;
- }
-
- @Override
- public boolean needsInput() {
- return false;
- }
-
- @Override
- public void onPause() {
- mResumed = false;
-
- if (mCountdownTimer != null) {
- mCountdownTimer.cancel();
- mCountdownTimer = null;
- }
- if (mPendingLockCheck != null) {
- mPendingLockCheck.cancel(false);
- mPendingLockCheck = null;
- }
- reset();
- }
-
- @Override
- public void onResume(int reason) {
- mResumed = true;
- }
-
- @Override
- public KeyguardSecurityCallback getCallback() {
- return mCallback;
- }
-
- @Override
- public void showPromptReason(int reason) {
- if (reason != PROMPT_REASON_NONE) {
- int promtReasonStringRes = getPromptReasonStringRes(reason);
- if (promtReasonStringRes != 0) {
- mSecurityMessageDisplay.setMessage(promtReasonStringRes);
- }
- }
- }
-
- @Override
- public void showMessage(CharSequence message, ColorStateList colorState) {
- if (colorState != null) {
- mSecurityMessageDisplay.setNextMessageColor(colorState);
- }
- mSecurityMessageDisplay.setMessage(message);
+ return mKeyDownListener != null && mKeyDownListener.onKeyDown(keyCode, event);
}
protected abstract int getPromptReasonStringRes(int reason);
@@ -333,9 +87,12 @@
}
}
- @Override
- public boolean startDisappearAnimation(Runnable finishRunnable) {
- return false;
+ public void setKeyDownListener(KeyDownListener keyDownListener) {
+ mKeyDownListener = keyDownListener;
+ }
+
+ public interface KeyDownListener {
+ boolean onKeyDown(int keyCode, KeyEvent keyEvent);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
new file mode 100644
index 0000000..d957628
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -0,0 +1,275 @@
+/*
+ * 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.keyguard;
+
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
+import static com.android.keyguard.KeyguardAbsKeyInputView.MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT;
+
+import android.content.res.ColorStateList;
+import android.os.AsyncTask;
+import android.os.CountDownTimer;
+import android.os.SystemClock;
+import android.view.KeyEvent;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternChecker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.keyguard.EmergencyButton.EmergencyButtonCallback;
+import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+
+public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKeyInputView>
+ extends KeyguardInputViewController<T> {
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final LockPatternUtils mLockPatternUtils;
+ private final LatencyTracker mLatencyTracker;
+ private CountDownTimer mCountdownTimer;
+ protected KeyguardMessageAreaController mMessageAreaController;
+ private boolean mDismissing;
+ protected AsyncTask<?, ?, ?> mPendingLockCheck;
+ protected boolean mResumed;
+
+ private final KeyDownListener mKeyDownListener = (keyCode, keyEvent) -> {
+ // Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN.
+ // We don't want to consider it valid user input because the UI
+ // will already respond to the event.
+ if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
+ onUserInput();
+ }
+ return false;
+ };
+
+ private final EmergencyButtonCallback mEmergencyButtonCallback = new EmergencyButtonCallback() {
+ @Override
+ public void onEmergencyButtonClickedWhenInCall() {
+ getKeyguardSecurityCallback().reset();
+ }
+ };
+
+ protected KeyguardAbsKeyInputViewController(T view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode,
+ LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ LatencyTracker latencyTracker) {
+ super(view, securityMode, keyguardSecurityCallback);
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mLockPatternUtils = lockPatternUtils;
+ mLatencyTracker = latencyTracker;
+ KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
+ mMessageAreaController = messageAreaControllerFactory.create(kma);
+ }
+
+ abstract void resetState();
+
+ @Override
+ public void init() {
+ super.init();
+ mMessageAreaController.init();
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mView.setKeyDownListener(mKeyDownListener);
+ mView.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled());
+ EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
+ if (button != null) {
+ button.setCallback(mEmergencyButtonCallback);
+ }
+ }
+
+ @Override
+ public void reset() {
+ // start fresh
+ mDismissing = false;
+ mView.resetPasswordText(false /* animate */, false /* announce */);
+ // if the user is currently locked out, enforce it.
+ long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
+ KeyguardUpdateMonitor.getCurrentUser());
+ if (shouldLockout(deadline)) {
+ handleAttemptLockout(deadline);
+ } else {
+ mView.resetState();
+ }
+ }
+
+ @Override
+ public boolean needsInput() {
+ return false;
+ }
+
+ @Override
+ public void showMessage(CharSequence message, ColorStateList colorState) {
+ if (colorState != null) {
+ mMessageAreaController.setNextMessageColor(colorState);
+ }
+ mMessageAreaController.setMessage(message);
+ }
+
+ // Allow subclasses to override this behavior
+ protected boolean shouldLockout(long deadline) {
+ return deadline != 0;
+ }
+
+ // Prevent user from using the PIN/Password entry until scheduled deadline.
+ protected void handleAttemptLockout(long elapsedRealtimeDeadline) {
+ mView.setPasswordEntryEnabled(false);
+ long elapsedRealtime = SystemClock.elapsedRealtime();
+ long secondsInFuture = (long) Math.ceil(
+ (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
+ mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
+ mMessageAreaController.setMessage(mView.getResources().getQuantityString(
+ R.plurals.kg_too_many_failed_attempts_countdown,
+ secondsRemaining, secondsRemaining));
+ }
+
+ @Override
+ public void onFinish() {
+ mMessageAreaController.setMessage("");
+ resetState();
+ }
+ }.start();
+ }
+
+ void onPasswordChecked(int userId, boolean matched, int timeoutMs, boolean isValidPassword) {
+ boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
+ if (matched) {
+ getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0);
+ if (dismissKeyguard) {
+ mDismissing = true;
+ getKeyguardSecurityCallback().dismiss(true, userId);
+ }
+ } else {
+ if (isValidPassword) {
+ getKeyguardSecurityCallback().reportUnlockAttempt(userId, false, timeoutMs);
+ if (timeoutMs > 0) {
+ long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
+ userId, timeoutMs);
+ handleAttemptLockout(deadline);
+ }
+ }
+ if (timeoutMs == 0) {
+ mMessageAreaController.setMessage(mView.getWrongPasswordStringId());
+ }
+ }
+ mView.resetPasswordText(true /* animate */, !matched /* announce deletion if no match */);
+ }
+
+ protected void verifyPasswordAndUnlock() {
+ if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
+
+ final LockscreenCredential password = mView.getEnteredCredential();
+ mView.setPasswordEntryInputEnabled(false);
+ if (mPendingLockCheck != null) {
+ mPendingLockCheck.cancel(false);
+ }
+
+ final int userId = KeyguardUpdateMonitor.getCurrentUser();
+ if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
+ // to avoid accidental lockout, only count attempts that are long enough to be a
+ // real password. This may require some tweaking.
+ mView.setPasswordEntryInputEnabled(true);
+ onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
+ password.zeroize();
+ return;
+ }
+
+ mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL);
+ mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+
+ mKeyguardUpdateMonitor.setCredentialAttempted();
+ mPendingLockCheck = LockPatternChecker.checkCredential(
+ mLockPatternUtils,
+ password,
+ userId,
+ new LockPatternChecker.OnCheckCallback() {
+
+ @Override
+ public void onEarlyMatched() {
+ mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL);
+
+ onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */,
+ true /* isValidPassword */);
+ password.zeroize();
+ }
+
+ @Override
+ public void onChecked(boolean matched, int timeoutMs) {
+ mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ mView.setPasswordEntryInputEnabled(true);
+ mPendingLockCheck = null;
+ if (!matched) {
+ onPasswordChecked(userId, false /* matched */, timeoutMs,
+ true /* isValidPassword */);
+ }
+ password.zeroize();
+ }
+
+ @Override
+ public void onCancelled() {
+ // We already got dismissed with the early matched callback, so we cancelled
+ // the check. However, we still need to note down the latency.
+ mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ password.zeroize();
+ }
+ });
+ }
+
+ @Override
+ public void showPromptReason(int reason) {
+ if (reason != PROMPT_REASON_NONE) {
+ int promtReasonStringRes = mView.getPromptReasonStringRes(reason);
+ if (promtReasonStringRes != 0) {
+ mMessageAreaController.setMessage(promtReasonStringRes);
+ }
+ }
+ }
+
+ protected void onUserInput() {
+ getKeyguardSecurityCallback().userActivity();
+ getKeyguardSecurityCallback().onUserInput();
+ mMessageAreaController.setMessage("");
+ }
+
+ @Override
+ public void onResume(int reason) {
+ mResumed = true;
+ }
+
+ @Override
+ public void onPause() {
+ mResumed = false;
+
+ if (mCountdownTimer != null) {
+ mCountdownTimer.cancel();
+ mCountdownTimer = null;
+ }
+ if (mPendingLockCheck != null) {
+ mPendingLockCheck.cancel(false);
+ mPendingLockCheck = null;
+ }
+ reset();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index be21d20..36d5543 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -39,7 +39,6 @@
import com.android.systemui.R;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
-import com.android.systemui.util.InjectionInflationController;
import javax.inject.Inject;
@@ -49,7 +48,6 @@
private final MediaRouter mMediaRouter;
private final DisplayManager mDisplayService;
- private final InjectionInflationController mInjectableInflater;
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
private final Context mContext;
@@ -92,10 +90,8 @@
@Inject
public KeyguardDisplayManager(Context context,
- InjectionInflationController injectableInflater,
KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) {
mContext = context;
- mInjectableInflater = injectableInflater;
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
mMediaRouter = mContext.getSystemService(MediaRouter.class);
mDisplayService = mContext.getSystemService(DisplayManager.class);
@@ -131,8 +127,7 @@
Presentation presentation = mPresentations.get(displayId);
if (presentation == null) {
final Presentation newPresentation = new KeyguardPresentation(mContext, display,
- mKeyguardStatusViewComponentFactory,
- mInjectableInflater.injectable(LayoutInflater.from(mContext)));
+ mKeyguardStatusViewComponentFactory, LayoutInflater.from(mContext));
newPresentation.setOnDismissListener(dialog -> {
if (newPresentation.equals(mPresentations.get(displayId))) {
mPresentations.remove(displayId);
@@ -250,7 +245,7 @@
private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
- private final LayoutInflater mInjectableLayoutInflater;
+ private final LayoutInflater mLayoutInflater;
private KeyguardClockSwitchController mKeyguardClockSwitchController;
private View mClock;
private int mUsableWidth;
@@ -270,10 +265,10 @@
KeyguardPresentation(Context context, Display display,
KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
- LayoutInflater injectionLayoutInflater) {
+ LayoutInflater layoutInflater) {
super(context, display, R.style.Theme_SystemUI_KeyguardPresentation);
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
- mInjectableLayoutInflater = injectionLayoutInflater;
+ mLayoutInflater = layoutInflater;
getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
setCancelable(false);
}
@@ -299,7 +294,7 @@
mMarginLeft = (100 - VIDEO_SAFE_REGION) * p.x / 200;
mMarginTop = (100 - VIDEO_SAFE_REGION) * p.y / 200;
- setContentView(mInjectableLayoutInflater.inflate(R.layout.keyguard_presentation, null));
+ setContentView(mLayoutInflater.inflate(R.layout.keyguard_presentation, null));
// Logic to make the lock screen fullscreen
getWindow().getDecorView().setSystemUiVisibility(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index 7aabb17..9ffa658 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -178,18 +178,18 @@
/** Initialize the Controller. */
public void init() {
super.init();
- mView.setViewMediatorCallback(mViewMediatorCallback);
- // Update ViewMediator with the current input method requirements
- mViewMediatorCallback.setNeedsInput(mKeyguardSecurityContainerController.needsInput());
mKeyguardSecurityContainerController.init();
- mKeyguardSecurityContainerController.setSecurityCallback(mSecurityCallback);
- mKeyguardSecurityContainerController.showPrimarySecurityScreen(false);
}
@Override
protected void onViewAttached() {
+ mView.setViewMediatorCallback(mViewMediatorCallback);
+ // Update ViewMediator with the current input method requirements
+ mViewMediatorCallback.setNeedsInput(mKeyguardSecurityContainerController.needsInput());
mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
mView.setOnKeyListener(mOnKeyListener);
+ mKeyguardSecurityContainerController.setSecurityCallback(mSecurityCallback);
+ mKeyguardSecurityContainerController.showPrimarySecurityScreen(false);
}
@Override
@@ -350,7 +350,7 @@
}
public boolean handleBackKey() {
- if (mKeyguardSecurityContainerController.getCurrentSecuritySelection()
+ if (mKeyguardSecurityContainerController.getCurrentSecurityMode()
!= SecurityMode.None) {
mKeyguardSecurityContainerController.dismiss(
false, KeyguardUpdateMonitor.getCurrentUser());
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
new file mode 100644
index 0000000..d42a53c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
@@ -0,0 +1,55 @@
+/*
+ * 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.keyguard;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.LinearLayout;
+
+import androidx.annotation.Nullable;
+
+/**
+ * A Base class for all Keyguard password/pattern/pin related inputs.
+ */
+public abstract class KeyguardInputView extends LinearLayout {
+
+ public KeyguardInputView(Context context) {
+ super(context);
+ }
+
+ public KeyguardInputView(Context context,
+ @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public KeyguardInputView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ abstract CharSequence getTitle();
+
+ boolean disallowInterceptTouch(MotionEvent event) {
+ return false;
+ }
+
+ void startAppearAnimation() {}
+
+ boolean startDisappearAnimation(Runnable finishRunnable) {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
new file mode 100644
index 0000000..fbda818
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -0,0 +1,196 @@
+/*
+ * 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.keyguard;
+
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.telephony.TelephonyManager;
+import android.view.inputmethod.InputMethodManager;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.ViewController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+
+import javax.inject.Inject;
+
+
+/** Controller for a {@link KeyguardSecurityView}. */
+public abstract class KeyguardInputViewController<T extends KeyguardInputView>
+ extends ViewController<T> implements KeyguardSecurityView {
+
+ private final SecurityMode mSecurityMode;
+ private final KeyguardSecurityCallback mKeyguardSecurityCallback;
+ private boolean mPaused;
+
+
+ // The following is used to ignore callbacks from SecurityViews that are no longer current
+ // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
+ // state for the current security method.
+ private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
+ @Override
+ public void userActivity() { }
+ @Override
+ public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { }
+ @Override
+ public boolean isVerifyUnlockOnly() {
+ return false;
+ }
+ @Override
+ public void dismiss(boolean securityVerified, int targetUserId) { }
+ @Override
+ public void dismiss(boolean authenticated, int targetId,
+ boolean bypassSecondaryLockScreen) { }
+ @Override
+ public void onUserInput() { }
+ @Override
+ public void reset() {}
+ };
+
+ protected KeyguardInputViewController(T view, SecurityMode securityMode,
+ KeyguardSecurityCallback keyguardSecurityCallback) {
+ super(view);
+ mSecurityMode = securityMode;
+ mKeyguardSecurityCallback = keyguardSecurityCallback;
+ }
+
+ @Override
+ protected void onViewAttached() {
+ }
+
+ @Override
+ protected void onViewDetached() {
+ }
+
+ SecurityMode getSecurityMode() {
+ return mSecurityMode;
+ }
+
+ protected KeyguardSecurityCallback getKeyguardSecurityCallback() {
+ if (mPaused) {
+ return mNullCallback;
+ }
+
+ return mKeyguardSecurityCallback;
+ }
+
+ @Override
+ public void reset() {
+ }
+
+ @Override
+ public void onPause() {
+ mPaused = true;
+ }
+
+ @Override
+ public void onResume(int reason) {
+ mPaused = false;
+ }
+
+ @Override
+ public void showPromptReason(int reason) {
+ }
+
+ @Override
+ public void showMessage(CharSequence message, ColorStateList colorState) {
+ }
+
+ public void startAppearAnimation() {
+ mView.startAppearAnimation();
+ }
+
+ public boolean startDisappearAnimation(Runnable finishRunnable) {
+ return mView.startDisappearAnimation(finishRunnable);
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mView.getTitle();
+ }
+
+ /** Finds the index of this view in the suppplied parent view. */
+ public int getIndexIn(KeyguardSecurityViewFlipper view) {
+ return view.indexOfChild(mView);
+ }
+
+ /** Factory for a {@link KeyguardInputViewController}. */
+ public static class Factory {
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final LockPatternUtils mLockPatternUtils;
+ private final LatencyTracker mLatencyTracker;
+ private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory;
+ private final InputMethodManager mInputMethodManager;
+ private final DelayableExecutor mMainExecutor;
+ private final Resources mResources;
+ private LiftToActivateListener mLiftToActivateListener;
+ private TelephonyManager mTelephonyManager;
+
+ @Inject
+ public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
+ LockPatternUtils lockPatternUtils,
+ LatencyTracker latencyTracker,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ InputMethodManager inputMethodManager, @Main DelayableExecutor mainExecutor,
+ @Main Resources resources, LiftToActivateListener liftToActivateListener,
+ TelephonyManager telephonyManager) {
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mLockPatternUtils = lockPatternUtils;
+ mLatencyTracker = latencyTracker;
+ mMessageAreaControllerFactory = messageAreaControllerFactory;
+ mInputMethodManager = inputMethodManager;
+ mMainExecutor = mainExecutor;
+ mResources = resources;
+ mLiftToActivateListener = liftToActivateListener;
+ mTelephonyManager = telephonyManager;
+ }
+
+ /** Create a new {@link KeyguardInputViewController}. */
+ public KeyguardInputViewController create(KeyguardInputView keyguardInputView,
+ SecurityMode securityMode, KeyguardSecurityCallback keyguardSecurityCallback) {
+ if (keyguardInputView instanceof KeyguardPatternView) {
+ return new KeyguardPatternViewController((KeyguardPatternView) keyguardInputView,
+ mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
+ keyguardSecurityCallback, mLatencyTracker, mMessageAreaControllerFactory);
+ } else if (keyguardInputView instanceof KeyguardPasswordView) {
+ return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView,
+ mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
+ keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
+ mInputMethodManager, mMainExecutor, mResources);
+ } else if (keyguardInputView instanceof KeyguardPINView) {
+ return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
+ mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
+ keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
+ mLiftToActivateListener);
+ } else if (keyguardInputView instanceof KeyguardSimPinView) {
+ return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
+ mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
+ keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
+ mLiftToActivateListener, mTelephonyManager);
+ } else if (keyguardInputView instanceof KeyguardSimPukView) {
+ return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
+ mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
+ keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
+ mLiftToActivateListener, mTelephonyManager);
+ }
+
+ throw new RuntimeException("Unable to find controller for " + keyguardInputView);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index a8b1451..1a0a437 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -16,8 +16,6 @@
package com.android.keyguard;
-import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
-
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
@@ -31,20 +29,14 @@
import android.view.View;
import android.widget.TextView;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import java.lang.ref.WeakReference;
-import javax.inject.Inject;
-import javax.inject.Named;
-
/***
* Manages a number of views inside of the given layout. See below for a list of widgets.
*/
-public class KeyguardMessageArea extends TextView implements SecurityMessageDisplay,
- ConfigurationController.ConfigurationListener {
+public class KeyguardMessageArea extends TextView implements SecurityMessageDisplay {
/** Handler token posted with accessibility announcement runnables. */
private static final Object ANNOUNCE_TOKEN = new Object();
@@ -56,71 +48,26 @@
private static final int DEFAULT_COLOR = -1;
private final Handler mHandler;
- private final ConfigurationController mConfigurationController;
private ColorStateList mDefaultColorState;
private CharSequence mMessage;
private ColorStateList mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
private boolean mBouncerVisible;
- private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
- public void onFinishedGoingToSleep(int why) {
- setSelected(false);
- }
-
- public void onStartedWakingUp() {
- setSelected(true);
- }
-
- @Override
- public void onKeyguardBouncerChanged(boolean bouncer) {
- mBouncerVisible = bouncer;
- update();
- }
- };
-
- public KeyguardMessageArea(Context context) {
- super(context, null);
- throw new IllegalStateException("This constructor should never be invoked");
- }
-
- @Inject
- public KeyguardMessageArea(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
- ConfigurationController configurationController) {
- this(context, attrs, Dependency.get(KeyguardUpdateMonitor.class), configurationController);
- }
-
- public KeyguardMessageArea(Context context, AttributeSet attrs, KeyguardUpdateMonitor monitor,
- ConfigurationController configurationController) {
+ public KeyguardMessageArea(Context context, AttributeSet attrs) {
super(context, attrs);
setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
- monitor.registerCallback(mInfoCallback);
mHandler = new Handler(Looper.myLooper());
- mConfigurationController = configurationController;
onThemeChanged();
}
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mConfigurationController.addCallback(this);
- onThemeChanged();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mConfigurationController.removeCallback(this);
- }
-
- @Override
public void setNextMessageColor(ColorStateList colorState) {
mNextMessageColorState = colorState;
}
- @Override
- public void onThemeChanged() {
+ void onThemeChanged() {
TypedArray array = mContext.obtainStyledAttributes(new int[] {
R.attr.wallpaperTextColor
});
@@ -130,8 +77,7 @@
update();
}
- @Override
- public void onDensityOrFontScaleChanged() {
+ void onDensityOrFontScaleChanged() {
TypedArray array = mContext.obtainStyledAttributes(R.style.Keyguard_TextView, new int[] {
android.R.attr.textSize
});
@@ -177,12 +123,6 @@
return messageArea;
}
- @Override
- protected void onFinishInflate() {
- boolean shouldMarquee = Dependency.get(KeyguardUpdateMonitor.class).isDeviceInteractive();
- setSelected(shouldMarquee); // This is required to ensure marquee works
- }
-
private void securityMessageChanged(CharSequence message) {
mMessage = message;
update();
@@ -196,7 +136,7 @@
update();
}
- private void update() {
+ void update() {
CharSequence status = mMessage;
setVisibility(TextUtils.isEmpty(status) || !mBouncerVisible ? INVISIBLE : VISIBLE);
setText(status);
@@ -208,6 +148,9 @@
setTextColor(colorState);
}
+ public void setBouncerVisible(boolean bouncerVisible) {
+ mBouncerVisible = bouncerVisible;
+ }
/**
* Runnable used to delay accessibility announcements.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index f056bdb..1618e8e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -16,7 +16,10 @@
package com.android.keyguard;
+import android.content.res.ColorStateList;
+
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
@@ -26,6 +29,35 @@
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ConfigurationController mConfigurationController;
+
+ private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
+ public void onFinishedGoingToSleep(int why) {
+ mView.setSelected(false);
+ }
+
+ public void onStartedWakingUp() {
+ mView.setSelected(true);
+ }
+
+ @Override
+ public void onKeyguardBouncerChanged(boolean bouncer) {
+ mView.setBouncerVisible(bouncer);
+ mView.update();
+ }
+ };
+
+ private ConfigurationListener mConfigurationListener = new ConfigurationListener() {
+ @Override
+ public void onThemeChanged() {
+ mView.onThemeChanged();
+ }
+
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ mView.onDensityOrFontScaleChanged();
+ }
+ };
+
private KeyguardMessageAreaController(KeyguardMessageArea view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
ConfigurationController configurationController) {
@@ -37,17 +69,31 @@
@Override
protected void onViewAttached() {
- //mConfigurationController.addCallback();
- //mKeyguardUpdateMonitor.registerCallback();
+ mConfigurationController.addCallback(mConfigurationListener);
+ mKeyguardUpdateMonitor.registerCallback(mInfoCallback);
+ mView.setSelected(mKeyguardUpdateMonitor.isDeviceInteractive());
+ mView.onThemeChanged();
}
@Override
protected void onViewDetached() {
- //mConfigurationController.removeCallback();
- //mKeyguardUpdateMonitor.removeCallback();
+ mConfigurationController.removeCallback(mConfigurationListener);
+ mKeyguardUpdateMonitor.removeCallback(mInfoCallback);
}
- /** Factory for createing {@link com.android.keyguard.KeyguardMessageAreaController}. */
+ public void setMessage(CharSequence s) {
+ mView.setMessage(s);
+ }
+
+ public void setMessage(int resId) {
+ mView.setMessage(resId);
+ }
+
+ public void setNextMessageColor(ColorStateList colorState) {
+ mView.setNextMessageColor(colorState);
+ }
+
+ /** Factory for creating {@link com.android.keyguard.KeyguardMessageAreaController}. */
public static class Factory {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ConfigurationController mConfigurationController;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 12ea1d5..580d704 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -24,7 +24,6 @@
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
/**
@@ -40,10 +39,8 @@
private ViewGroup mRow1;
private ViewGroup mRow2;
private ViewGroup mRow3;
- private View mDivider;
private int mDisappearYTranslation;
private View[][] mViews;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
public KeyguardPINView(Context context) {
this(context, null);
@@ -63,15 +60,10 @@
mContext, android.R.interpolator.fast_out_linear_in));
mDisappearYTranslation = getResources().getDimensionPixelSize(
R.dimen.disappear_y_translation);
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
}
@Override
protected void resetState() {
- super.resetState();
- if (mSecurityMessageDisplay != null) {
- mSecurityMessageDisplay.setMessage("");
- }
}
@Override
@@ -88,7 +80,6 @@
mRow1 = findViewById(R.id.row1);
mRow2 = findViewById(R.id.row2);
mRow3 = findViewById(R.id.row3);
- mDivider = findViewById(R.id.divider);
mViews = new View[][]{
new View[]{
mRow0, null, null
@@ -112,18 +103,6 @@
new View[]{
null, mEcaView, null
}};
-
- View cancelBtn = findViewById(R.id.cancel_button);
- if (cancelBtn != null) {
- cancelBtn.setOnClickListener(view -> {
- mCallback.reset();
- mCallback.onCancelClicked();
- });
- }
- }
-
- @Override
- public void showUsabilityHint() {
}
@Override
@@ -147,24 +126,21 @@
});
}
- @Override
- public boolean startDisappearAnimation(final Runnable finishRunnable) {
+ public boolean startDisappearAnimation(boolean needsSlowUnlockTransition,
+ final Runnable finishRunnable) {
+
enableClipping(false);
setTranslationY(0);
AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 280 /* duration */,
mDisappearYTranslation, mDisappearAnimationUtils.getInterpolator());
- DisappearAnimationUtils disappearAnimationUtils = mKeyguardUpdateMonitor
- .needsSlowUnlockTransition()
+ DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition
? mDisappearAnimationUtilsLocked
: mDisappearAnimationUtils;
disappearAnimationUtils.startAnimation2d(mViews,
- new Runnable() {
- @Override
- public void run() {
- enableClipping(true);
- if (finishRunnable != null) {
- finishRunnable.run();
- }
+ () -> {
+ enableClipping(true);
+ if (finishRunnable != null) {
+ finishRunnable.run();
}
});
return true;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 97317cf..aaa5efe 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -16,50 +16,37 @@
package com.android.keyguard;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
+
import android.content.Context;
import android.graphics.Rect;
-import android.os.UserHandle;
-import android.text.Editable;
-import android.text.InputType;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.text.method.TextKeyListener;
import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
import android.widget.TextView;
-import android.widget.TextView.OnEditorActionListener;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.TextViewInputDisabler;
import com.android.systemui.R;
-
-import java.util.List;
/**
* Displays an alphanumeric (latin-1) key entry for the user to enter
* an unlock password
*/
-public class KeyguardPasswordView extends KeyguardAbsKeyInputView
- implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
+public class KeyguardPasswordView extends KeyguardAbsKeyInputView {
- private final boolean mShowImeAtScreenOn;
private final int mDisappearYTranslation;
// A delay constant to be used in a workaround for the situation where InputMethodManagerService
// is not switched to the new user yet.
// TODO: Remove this by ensuring such a race condition never happens.
- private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms
- InputMethodManager mImm;
private TextView mPasswordEntry;
private TextViewInputDisabler mPasswordEntryDisabler;
- private View mSwitchImeButton;
private Interpolator mLinearOutSlowInInterpolator;
private Interpolator mFastOutLinearInInterpolator;
@@ -70,8 +57,6 @@
public KeyguardPasswordView(Context context, AttributeSet attrs) {
super(context, attrs);
- mShowImeAtScreenOn = context.getResources().
- getBoolean(R.bool.kg_show_ime_at_screen_on);
mDisappearYTranslation = getResources().getDimensionPixelSize(
R.dimen.disappear_y_translation);
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
@@ -82,20 +67,6 @@
@Override
protected void resetState() {
- mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
- if (mSecurityMessageDisplay != null) {
- mSecurityMessageDisplay.setMessage("");
- }
- final boolean wasDisabled = mPasswordEntry.isEnabled();
- setPasswordEntryEnabled(true);
- setPasswordEntryInputEnabled(true);
- // Don't call showSoftInput when PasswordEntry is invisible or in pausing stage.
- if (!mResumed || !mPasswordEntry.isVisibleToUser()) {
- return;
- }
- if (wasDisabled) {
- mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
- }
}
@Override
@@ -104,29 +75,6 @@
}
@Override
- public boolean needsInput() {
- return true;
- }
-
- @Override
- public void onResume(final int reason) {
- super.onResume(reason);
-
- // Wait a bit to focus the field so the focusable flag on the window is already set then.
- post(new Runnable() {
- @Override
- public void run() {
- if (isShown() && mPasswordEntry.isEnabled()) {
- mPasswordEntry.requestFocus();
- if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
- mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
- }
- }
- }
- });
- }
-
- @Override
protected int getPromptReasonStringRes(int reason) {
switch (reason) {
case PROMPT_REASON_RESTART:
@@ -146,97 +94,13 @@
}
}
- @Override
- public void onPause() {
- super.onPause();
- mImm.hideSoftInputFromWindow(getWindowToken(), 0);
- }
-
- @Override
- public void onStartingToHide() {
- mImm.hideSoftInputFromWindow(getWindowToken(), 0);
- }
-
- private void updateSwitchImeButton() {
- // If there's more than one IME, enable the IME switcher button
- final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE;
- final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes(mImm, false);
- if (wasVisible != shouldBeVisible) {
- mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE);
- }
-
- // TODO: Check if we still need this hack.
- // If no icon is visible, reset the start margin on the password field so the text is
- // still centered.
- if (mSwitchImeButton.getVisibility() != View.VISIBLE) {
- android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams();
- if (params instanceof MarginLayoutParams) {
- final MarginLayoutParams mlp = (MarginLayoutParams) params;
- mlp.setMarginStart(0);
- mPasswordEntry.setLayoutParams(params);
- }
- }
- }
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mImm = (InputMethodManager) getContext().getSystemService(
- Context.INPUT_METHOD_SERVICE);
-
mPasswordEntry = findViewById(getPasswordTextViewId());
- mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
mPasswordEntryDisabler = new TextViewInputDisabler(mPasswordEntry);
- mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
- mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
- | InputType.TYPE_TEXT_VARIATION_PASSWORD);
- mPasswordEntry.setOnEditorActionListener(this);
- mPasswordEntry.addTextChangedListener(this);
-
- // Poke the wakelock any time the text is selected or modified
- mPasswordEntry.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mCallback.userActivity();
- }
- });
-
- // Set selected property on so the view can send accessibility events.
- mPasswordEntry.setSelected(true);
-
- mSwitchImeButton = findViewById(R.id.switch_ime_button);
- mSwitchImeButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mCallback.userActivity(); // Leave the screen on a bit longer
- // Do not show auxiliary subtypes in password lock screen.
- mImm.showInputMethodPickerFromSystem(false /* showAuxiliarySubtypes */,
- getContext().getDisplayId());
- }
- });
-
- View cancelBtn = findViewById(R.id.cancel_button);
- if (cancelBtn != null) {
- cancelBtn.setOnClickListener(view -> {
- mCallback.reset();
- mCallback.onCancelClicked();
- });
- }
-
- // If there's more than one IME, enable the IME switcher button
- updateSwitchImeButton();
-
- // When we the current user is switching, InputMethodManagerService sometimes has not
- // switched internal state yet here. As a quick workaround, we check the keyboard state
- // again.
- // TODO: Remove this workaround by ensuring such a race condition never happens.
- postDelayed(new Runnable() {
- @Override
- public void run() {
- updateSwitchImeButton();
- }
- }, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON);
}
@Override
@@ -265,59 +129,6 @@
mPasswordEntryDisabler.setInputEnabled(enabled);
}
- /**
- * Method adapted from com.android.inputmethod.latin.Utils
- *
- * @param imm The input method manager
- * @param shouldIncludeAuxiliarySubtypes
- * @return true if we have multiple IMEs to choose from
- */
- private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm,
- final boolean shouldIncludeAuxiliarySubtypes) {
- final List<InputMethodInfo> enabledImis =
- imm.getEnabledInputMethodListAsUser(KeyguardUpdateMonitor.getCurrentUser());
-
- // Number of the filtered IMEs
- int filteredImisCount = 0;
-
- for (InputMethodInfo imi : enabledImis) {
- // We can return true immediately after we find two or more filtered IMEs.
- if (filteredImisCount > 1) return true;
- final List<InputMethodSubtype> subtypes =
- imm.getEnabledInputMethodSubtypeList(imi, true);
- // IMEs that have no subtypes should be counted.
- if (subtypes.isEmpty()) {
- ++filteredImisCount;
- continue;
- }
-
- int auxCount = 0;
- for (InputMethodSubtype subtype : subtypes) {
- if (subtype.isAuxiliary()) {
- ++auxCount;
- }
- }
- final int nonAuxCount = subtypes.size() - auxCount;
-
- // IMEs that have one or more non-auxiliary subtypes should be counted.
- // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
- // subtypes should be counted as well.
- if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
- ++filteredImisCount;
- continue;
- }
- }
-
- return filteredImisCount > 1
- // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled
- // input method subtype (The current IME should be LatinIME.)
- || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
- }
-
- @Override
- public void showUsabilityHint() {
- }
-
@Override
public int getWrongPasswordStringId() {
return R.string.kg_wrong_password;
@@ -346,45 +157,8 @@
}
@Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- if (mCallback != null) {
- mCallback.userActivity();
- }
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- // Poor man's user edit detection, assuming empty text is programmatic and everything else
- // is from the user.
- if (!TextUtils.isEmpty(s)) {
- onUserInput();
- }
- }
-
- @Override
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- // Check if this was the result of hitting the enter key
- final boolean isSoftImeEvent = event == null
- && (actionId == EditorInfo.IME_NULL
- || actionId == EditorInfo.IME_ACTION_DONE
- || actionId == EditorInfo.IME_ACTION_NEXT);
- final boolean isKeyboardEnterKey = event != null
- && KeyEvent.isConfirmKey(event.getKeyCode())
- && event.getAction() == KeyEvent.ACTION_DOWN;
- if (isSoftImeEvent || isKeyboardEnterKey) {
- verifyPasswordAndUnlock();
- return true;
- }
- return false;
- }
-
- @Override
public CharSequence getTitle() {
- return getContext().getString(
+ return getResources().getString(
com.android.internal.R.string.keyguard_accessibility_password_unlock);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
new file mode 100644
index 0000000..d34ea8c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -0,0 +1,275 @@
+/*
+ * 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.keyguard;
+
+import android.content.res.Resources;
+import android.os.UserHandle;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.text.method.TextKeyListener;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+
+import java.util.List;
+
+public class KeyguardPasswordViewController
+ extends KeyguardAbsKeyInputViewController<KeyguardPasswordView> {
+
+ private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms
+
+ private final KeyguardSecurityCallback mKeyguardSecurityCallback;
+ private final InputMethodManager mInputMethodManager;
+ private final DelayableExecutor mMainExecutor;
+ private final boolean mShowImeAtScreenOn;
+ private TextView mPasswordEntry;
+ private View mSwitchImeButton;
+
+ private final OnEditorActionListener mOnEditorActionListener = (v, actionId, event) -> {
+ // Check if this was the result of hitting the enter key
+ final boolean isSoftImeEvent = event == null
+ && (actionId == EditorInfo.IME_NULL
+ || actionId == EditorInfo.IME_ACTION_DONE
+ || actionId == EditorInfo.IME_ACTION_NEXT);
+ final boolean isKeyboardEnterKey = event != null
+ && KeyEvent.isConfirmKey(event.getKeyCode())
+ && event.getAction() == KeyEvent.ACTION_DOWN;
+ if (isSoftImeEvent || isKeyboardEnterKey) {
+ verifyPasswordAndUnlock();
+ return true;
+ }
+ return false;
+ };
+
+ private final TextWatcher mTextWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ mKeyguardSecurityCallback.userActivity();
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (!TextUtils.isEmpty(s)) {
+ onUserInput();
+ }
+ }
+ };
+
+ protected KeyguardPasswordViewController(KeyguardPasswordView view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode,
+ LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ LatencyTracker latencyTracker,
+ InputMethodManager inputMethodManager,
+ @Main DelayableExecutor mainExecutor,
+ @Main Resources resources) {
+ super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
+ messageAreaControllerFactory, latencyTracker);
+ mKeyguardSecurityCallback = keyguardSecurityCallback;
+ mInputMethodManager = inputMethodManager;
+ mMainExecutor = mainExecutor;
+ mShowImeAtScreenOn = resources.getBoolean(R.bool.kg_show_ime_at_screen_on);
+ mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
+ mSwitchImeButton = mView.findViewById(R.id.switch_ime_button);
+ }
+
+ @Override
+ protected void onViewAttached() {
+ super.onViewAttached();
+ mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
+ mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
+ mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+
+ // Set selected property on so the view can send accessibility events.
+ mPasswordEntry.setSelected(true);
+ mPasswordEntry.setOnEditorActionListener(mOnEditorActionListener);
+ mPasswordEntry.addTextChangedListener(mTextWatcher);
+ // Poke the wakelock any time the text is selected or modified
+ mPasswordEntry.setOnClickListener(v -> mKeyguardSecurityCallback.userActivity());
+
+ mSwitchImeButton.setOnClickListener(v -> {
+ mKeyguardSecurityCallback.userActivity(); // Leave the screen on a bit longer
+ // Do not show auxiliary subtypes in password lock screen.
+ mInputMethodManager.showInputMethodPickerFromSystem(false,
+ mView.getContext().getDisplayId());
+ });
+
+ View cancelBtn = mView.findViewById(R.id.cancel_button);
+ if (cancelBtn != null) {
+ cancelBtn.setOnClickListener(view -> {
+ mKeyguardSecurityCallback.reset();
+ mKeyguardSecurityCallback.onCancelClicked();
+ });
+ }
+
+ // If there's more than one IME, enable the IME switcher button
+ updateSwitchImeButton();
+
+ // When we the current user is switching, InputMethodManagerService sometimes has not
+ // switched internal state yet here. As a quick workaround, we check the keyboard state
+ // again.
+ // TODO: Remove this workaround by ensuring such a race condition never happens.
+ mMainExecutor.executeDelayed(
+ this::updateSwitchImeButton, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ super.onViewDetached();
+ mPasswordEntry.setOnEditorActionListener(null);
+ }
+
+ @Override
+ public boolean needsInput() {
+ return true;
+ }
+
+ @Override
+ void resetState() {
+ mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
+ mMessageAreaController.setMessage("");
+ final boolean wasDisabled = mPasswordEntry.isEnabled();
+ mView.setPasswordEntryEnabled(true);
+ mView.setPasswordEntryInputEnabled(true);
+ // Don't call showSoftInput when PasswordEntry is invisible or in pausing stage.
+ if (!mResumed || !mPasswordEntry.isVisibleToUser()) {
+ return;
+ }
+ if (wasDisabled) {
+ mInputMethodManager.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
+ }
+ }
+
+ @Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ // Wait a bit to focus the field so the focusable flag on the window is already set then.
+ mMainExecutor.execute(() -> {
+ if (mView.isShown() && mPasswordEntry.isEnabled()) {
+ mPasswordEntry.requestFocus();
+ if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
+ mInputMethodManager.showSoftInput(
+ mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0);
+ }
+
+ @Override
+ public void onStartingToHide() {
+ mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0);
+ }
+
+ private void updateSwitchImeButton() {
+ // If there's more than one IME, enable the IME switcher button
+ final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE;
+ final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes(
+ mInputMethodManager, false);
+ if (wasVisible != shouldBeVisible) {
+ mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE);
+ }
+
+ // TODO: Check if we still need this hack.
+ // If no icon is visible, reset the start margin on the password field so the text is
+ // still centered.
+ if (mSwitchImeButton.getVisibility() != View.VISIBLE) {
+ android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams();
+ if (params instanceof MarginLayoutParams) {
+ final MarginLayoutParams mlp = (MarginLayoutParams) params;
+ mlp.setMarginStart(0);
+ mPasswordEntry.setLayoutParams(params);
+ }
+ }
+ }
+
+ /**
+ * Method adapted from com.android.inputmethod.latin.Utils
+ *
+ * @param imm The input method manager
+ * @param shouldIncludeAuxiliarySubtypes
+ * @return true if we have multiple IMEs to choose from
+ */
+ private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm,
+ final boolean shouldIncludeAuxiliarySubtypes) {
+ final List<InputMethodInfo> enabledImis =
+ imm.getEnabledInputMethodListAsUser(KeyguardUpdateMonitor.getCurrentUser());
+
+ // Number of the filtered IMEs
+ int filteredImisCount = 0;
+
+ for (InputMethodInfo imi : enabledImis) {
+ // We can return true immediately after we find two or more filtered IMEs.
+ if (filteredImisCount > 1) return true;
+ final List<InputMethodSubtype> subtypes =
+ imm.getEnabledInputMethodSubtypeList(imi, true);
+ // IMEs that have no subtypes should be counted.
+ if (subtypes.isEmpty()) {
+ ++filteredImisCount;
+ continue;
+ }
+
+ int auxCount = 0;
+ for (InputMethodSubtype subtype : subtypes) {
+ if (subtype.isAuxiliary()) {
+ ++auxCount;
+ }
+ }
+ final int nonAuxCount = subtypes.size() - auxCount;
+
+ // IMEs that have one or more non-auxiliary subtypes should be counted.
+ // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
+ // subtypes should be counted as well.
+ if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
+ ++filteredImisCount;
+ continue;
+ }
+ }
+
+ return filteredImisCount > 1
+ // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's
+ //enabled input method subtype (The current IME should be LatinIME.)
+ || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index c4a9fcb..bdcf467 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -15,62 +15,39 @@
*/
package com.android.keyguard;
-import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
-import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
-
import android.content.Context;
-import android.content.res.ColorStateList;
import android.graphics.Rect;
-import android.os.AsyncTask;
-import android.os.CountDownTimer;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
-import android.widget.LinearLayout;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.LatencyTracker;
-import com.android.internal.widget.LockPatternChecker;
-import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView;
-import com.android.internal.widget.LockscreenCredential;
import com.android.settingslib.animation.AppearAnimationCreator;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
-import java.util.List;
-
-public class KeyguardPatternView extends LinearLayout implements KeyguardSecurityView,
- AppearAnimationCreator<LockPatternView.CellState>,
- EmergencyButton.EmergencyButtonCallback {
+public class KeyguardPatternView extends KeyguardInputView
+ implements AppearAnimationCreator<LockPatternView.CellState> {
private static final String TAG = "SecurityPatternView";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
- // how long before we clear the wrong pattern
- private static final int PATTERN_CLEAR_TIMEOUT_MS = 2000;
// how long we stay awake after each key beyond MIN_PATTERN_BEFORE_POKE_WAKELOCK
private static final int UNLOCK_PATTERN_WAKE_INTERVAL_MS = 7000;
- // how many cells the user has to cross before we poke the wakelock
- private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
-
// How much we scale up the duration of the disappear animation when the current user is locked
public static final float DISAPPEAR_MULTIPLIER_LOCKED = 1.5f;
// Extra padding, in pixels, that should eat touch events.
private static final int PATTERNS_TOUCH_AREA_EXTENSION = 40;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final AppearAnimationUtils mAppearAnimationUtils;
private final DisappearAnimationUtils mDisappearAnimationUtils;
private final DisappearAnimationUtils mDisappearAnimationUtilsLocked;
@@ -78,11 +55,7 @@
private final Rect mTempRect = new Rect();
private final Rect mLockPatternScreenBounds = new Rect();
- private CountDownTimer mCountdownTimer = null;
- private LockPatternUtils mLockPatternUtils;
- private AsyncTask<?, ?, ?> mPendingLockCheck;
private LockPatternView mLockPatternView;
- private KeyguardSecurityCallback mCallback;
/**
* Keeps track of the last time we poked the wake lock during dispatching of the touch event.
@@ -92,26 +65,9 @@
*/
private long mLastPokeTime = -UNLOCK_PATTERN_WAKE_INTERVAL_MS;
- /**
- * Useful for clearing out the wrong pattern after a delay
- */
- private Runnable mCancelPatternRunnable = new Runnable() {
- @Override
- public void run() {
- mLockPatternView.clearPattern();
- }
- };
- @VisibleForTesting
KeyguardMessageArea mSecurityMessageDisplay;
private View mEcaView;
private ViewGroup mContainer;
- private int mDisappearYTranslation;
-
- enum FooterMode {
- Normal,
- ForgotLockPattern,
- VerifyUnlocked
- }
public KeyguardPatternView(Context context) {
this(context, null);
@@ -119,7 +75,6 @@
public KeyguardPatternView(Context context, AttributeSet attrs) {
super(context, attrs);
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
mAppearAnimationUtils = new AppearAnimationUtils(context,
AppearAnimationUtils.DEFAULT_APPEAR_DURATION, 1.5f /* translationScale */,
2.0f /* delayScale */, AnimationUtils.loadInterpolator(
@@ -132,50 +87,16 @@
(long) (125 * DISAPPEAR_MULTIPLIER_LOCKED), 1.2f /* translationScale */,
0.6f /* delayScale */, AnimationUtils.loadInterpolator(
mContext, android.R.interpolator.fast_out_linear_in));
- mDisappearYTranslation = getResources().getDimensionPixelSize(
- R.dimen.disappear_y_translation);
- }
-
- @Override
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- mCallback = callback;
- }
-
- @Override
- public void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mLockPatternUtils = mLockPatternUtils == null
- ? new LockPatternUtils(mContext) : mLockPatternUtils;
mLockPatternView = findViewById(R.id.lockPatternView);
- mLockPatternView.setSaveEnabled(false);
- mLockPatternView.setOnPatternListener(new UnlockPatternListener());
- mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
- KeyguardUpdateMonitor.getCurrentUser()));
-
- // vibrate mode will be the same for the life of this screen
- mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
mEcaView = findViewById(R.id.keyguard_selector_fade_container);
mContainer = findViewById(R.id.container);
-
- EmergencyButton button = findViewById(R.id.emergency_call_button);
- if (button != null) {
- button.setCallback(this);
- }
-
- View cancelBtn = findViewById(R.id.cancel_button);
- if (cancelBtn != null) {
- cancelBtn.setOnClickListener(view -> {
- mCallback.reset();
- mCallback.onCancelClicked();
- });
- }
}
@Override
@@ -185,11 +106,6 @@
}
@Override
- public void onEmergencyButtonClickedWhenInCall() {
- mCallback.reset();
- }
-
- @Override
public boolean onTouchEvent(MotionEvent ev) {
boolean result = super.onTouchEvent(ev);
// as long as the user is entering a pattern (i.e sending a touch event that was handled
@@ -217,248 +133,11 @@
}
@Override
- public void reset() {
- // reset lock pattern
- mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
- KeyguardUpdateMonitor.getCurrentUser()));
- mLockPatternView.enableInput();
- mLockPatternView.setEnabled(true);
- mLockPatternView.clearPattern();
-
- if (mSecurityMessageDisplay == null) {
- return;
- }
-
- // if the user is currently locked out, enforce it.
- long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
- KeyguardUpdateMonitor.getCurrentUser());
- if (deadline != 0) {
- handleAttemptLockout(deadline);
- } else {
- displayDefaultSecurityMessage();
- }
- }
-
- private void displayDefaultSecurityMessage() {
- if (mSecurityMessageDisplay != null) {
- mSecurityMessageDisplay.setMessage("");
- }
- }
-
- @Override
- public void showUsabilityHint() {
- }
-
- @Override
- public boolean disallowInterceptTouch(MotionEvent event) {
+ boolean disallowInterceptTouch(MotionEvent event) {
return !mLockPatternView.isEmpty()
|| mLockPatternScreenBounds.contains((int) event.getRawX(), (int) event.getRawY());
}
- /** TODO: hook this up */
- public void cleanUp() {
- if (DEBUG) Log.v(TAG, "Cleanup() called on " + this);
- mLockPatternUtils = null;
- mLockPatternView.setOnPatternListener(null);
- }
-
- private class UnlockPatternListener implements LockPatternView.OnPatternListener {
-
- @Override
- public void onPatternStart() {
- mLockPatternView.removeCallbacks(mCancelPatternRunnable);
- mSecurityMessageDisplay.setMessage("");
- }
-
- @Override
- public void onPatternCleared() {
- }
-
- @Override
- public void onPatternCellAdded(List<LockPatternView.Cell> pattern) {
- mCallback.userActivity();
- mCallback.onUserInput();
- }
-
- @Override
- public void onPatternDetected(final List<LockPatternView.Cell> pattern) {
- mKeyguardUpdateMonitor.setCredentialAttempted();
- mLockPatternView.disableInput();
- if (mPendingLockCheck != null) {
- mPendingLockCheck.cancel(false);
- }
-
- final int userId = KeyguardUpdateMonitor.getCurrentUser();
- if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
- mLockPatternView.enableInput();
- onPatternChecked(userId, false, 0, false /* not valid - too short */);
- return;
- }
-
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
- LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
- }
- mPendingLockCheck = LockPatternChecker.checkCredential(
- mLockPatternUtils,
- LockscreenCredential.createPattern(pattern),
- userId,
- new LockPatternChecker.OnCheckCallback() {
-
- @Override
- public void onEarlyMatched() {
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(
- ACTION_CHECK_CREDENTIAL);
- }
- onPatternChecked(userId, true /* matched */, 0 /* timeoutMs */,
- true /* isValidPattern */);
- }
-
- @Override
- public void onChecked(boolean matched, int timeoutMs) {
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(
- ACTION_CHECK_CREDENTIAL_UNLOCKED);
- }
- mLockPatternView.enableInput();
- mPendingLockCheck = null;
- if (!matched) {
- onPatternChecked(userId, false /* matched */, timeoutMs,
- true /* isValidPattern */);
- }
- }
-
- @Override
- public void onCancelled() {
- // We already got dismissed with the early matched callback, so we
- // cancelled the check. However, we still need to note down the latency.
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(
- ACTION_CHECK_CREDENTIAL_UNLOCKED);
- }
- }
- });
- if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
- mCallback.userActivity();
- mCallback.onUserInput();
- }
- }
-
- private void onPatternChecked(int userId, boolean matched, int timeoutMs,
- boolean isValidPattern) {
- boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
- if (matched) {
- mCallback.reportUnlockAttempt(userId, true, 0);
- if (dismissKeyguard) {
- mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
- mCallback.dismiss(true, userId);
- }
- } else {
- mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
- if (isValidPattern) {
- mCallback.reportUnlockAttempt(userId, false, timeoutMs);
- if (timeoutMs > 0) {
- long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
- userId, timeoutMs);
- handleAttemptLockout(deadline);
- }
- }
- if (timeoutMs == 0) {
- mSecurityMessageDisplay.setMessage(R.string.kg_wrong_pattern);
- mLockPatternView.postDelayed(mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS);
- }
- }
- }
- }
-
- private void handleAttemptLockout(long elapsedRealtimeDeadline) {
- mLockPatternView.clearPattern();
- mLockPatternView.setEnabled(false);
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- final long secondsInFuture = (long) Math.ceil(
- (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
- mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {
-
- @Override
- public void onTick(long millisUntilFinished) {
- final int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
- mSecurityMessageDisplay.setMessage(mContext.getResources().getQuantityString(
- R.plurals.kg_too_many_failed_attempts_countdown,
- secondsRemaining, secondsRemaining));
- }
-
- @Override
- public void onFinish() {
- mLockPatternView.setEnabled(true);
- displayDefaultSecurityMessage();
- }
-
- }.start();
- }
-
- @Override
- public boolean needsInput() {
- return false;
- }
-
- @Override
- public void onPause() {
- if (mCountdownTimer != null) {
- mCountdownTimer.cancel();
- mCountdownTimer = null;
- }
- if (mPendingLockCheck != null) {
- mPendingLockCheck.cancel(false);
- mPendingLockCheck = null;
- }
- displayDefaultSecurityMessage();
- }
-
- @Override
- public void onResume(int reason) {
- }
-
- @Override
- public KeyguardSecurityCallback getCallback() {
- return mCallback;
- }
-
- @Override
- public void showPromptReason(int reason) {
- switch (reason) {
- case PROMPT_REASON_RESTART:
- mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_restart_pattern);
- break;
- case PROMPT_REASON_TIMEOUT:
- mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern);
- break;
- case PROMPT_REASON_DEVICE_ADMIN:
- mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_device_admin);
- break;
- case PROMPT_REASON_USER_REQUEST:
- mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_user_request);
- break;
- case PROMPT_REASON_PREPARE_FOR_UPDATE:
- mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern);
- break;
- case PROMPT_REASON_NONE:
- break;
- default:
- mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern);
- break;
- }
- }
-
- @Override
- public void showMessage(CharSequence message, ColorStateList colorState) {
- if (colorState != null) {
- mSecurityMessageDisplay.setNextMessageColor(colorState);
- }
- mSecurityMessageDisplay.setMessage(message);
- }
-
- @Override
public void startAppearAnimation() {
enableClipping(false);
setAlpha(1f);
@@ -467,12 +146,7 @@
0, mAppearAnimationUtils.getInterpolator());
mAppearAnimationUtils.startAnimation2d(
mLockPatternView.getCellStates(),
- new Runnable() {
- @Override
- public void run() {
- enableClipping(true);
- }
- },
+ () -> enableClipping(true),
this);
if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) {
mAppearAnimationUtils.createAnimation(mSecurityMessageDisplay, 0,
@@ -484,11 +158,9 @@
}
}
- @Override
- public boolean startDisappearAnimation(final Runnable finishRunnable) {
- float durationMultiplier = mKeyguardUpdateMonitor.needsSlowUnlockTransition()
- ? DISAPPEAR_MULTIPLIER_LOCKED
- : 1f;
+ public boolean startDisappearAnimation(boolean needsSlowUnlockTransition,
+ final Runnable finishRunnable) {
+ float durationMultiplier = needsSlowUnlockTransition ? DISAPPEAR_MULTIPLIER_LOCKED : 1f;
mLockPatternView.clearPattern();
enableClipping(false);
setTranslationY(0);
@@ -497,10 +169,8 @@
-mDisappearAnimationUtils.getStartTranslation(),
mDisappearAnimationUtils.getInterpolator());
- DisappearAnimationUtils disappearAnimationUtils = mKeyguardUpdateMonitor
- .needsSlowUnlockTransition()
- ? mDisappearAnimationUtilsLocked
- : mDisappearAnimationUtils;
+ DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition
+ ? mDisappearAnimationUtilsLocked : mDisappearAnimationUtils;
disappearAnimationUtils.startAnimation2d(mLockPatternView.getCellStates(),
() -> {
enableClipping(true);
@@ -549,7 +219,7 @@
@Override
public CharSequence getTitle() {
- return getContext().getString(
+ return getResources().getString(
com.android.internal.R.string.keyguard_accessibility_pattern_unlock);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
new file mode 100644
index 0000000..3db9db7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -0,0 +1,349 @@
+/*
+ * 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.keyguard;
+
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
+
+import android.content.res.ColorStateList;
+import android.os.AsyncTask;
+import android.os.CountDownTimer;
+import android.os.SystemClock;
+import android.view.View;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternChecker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockPatternView.Cell;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.keyguard.EmergencyButton.EmergencyButtonCallback;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+
+import java.util.List;
+
+public class KeyguardPatternViewController
+ extends KeyguardInputViewController<KeyguardPatternView> {
+
+ // how many cells the user has to cross before we poke the wakelock
+ private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
+
+ // how long before we clear the wrong pattern
+ private static final int PATTERN_CLEAR_TIMEOUT_MS = 2000;
+
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final LockPatternUtils mLockPatternUtils;
+ private final LatencyTracker mLatencyTracker;
+ private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory;
+
+ private KeyguardMessageAreaController mMessageAreaController;
+ private LockPatternView mLockPatternView;
+ private CountDownTimer mCountdownTimer;
+ private AsyncTask<?, ?, ?> mPendingLockCheck;
+
+ private EmergencyButtonCallback mEmergencyButtonCallback = new EmergencyButtonCallback() {
+ @Override
+ public void onEmergencyButtonClickedWhenInCall() {
+ getKeyguardSecurityCallback().reset();
+ }
+ };
+
+ /**
+ * Useful for clearing out the wrong pattern after a delay
+ */
+ private Runnable mCancelPatternRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mLockPatternView.clearPattern();
+ }
+ };
+
+ private class UnlockPatternListener implements LockPatternView.OnPatternListener {
+
+ @Override
+ public void onPatternStart() {
+ mLockPatternView.removeCallbacks(mCancelPatternRunnable);
+ mMessageAreaController.setMessage("");
+ }
+
+ @Override
+ public void onPatternCleared() {
+ }
+
+ @Override
+ public void onPatternCellAdded(List<Cell> pattern) {
+ getKeyguardSecurityCallback().userActivity();
+ getKeyguardSecurityCallback().onUserInput();
+ }
+
+ @Override
+ public void onPatternDetected(final List<LockPatternView.Cell> pattern) {
+ mKeyguardUpdateMonitor.setCredentialAttempted();
+ mLockPatternView.disableInput();
+ if (mPendingLockCheck != null) {
+ mPendingLockCheck.cancel(false);
+ }
+
+ final int userId = KeyguardUpdateMonitor.getCurrentUser();
+ if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
+ mLockPatternView.enableInput();
+ onPatternChecked(userId, false, 0, false /* not valid - too short */);
+ return;
+ }
+
+ mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL);
+ mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ mPendingLockCheck = LockPatternChecker.checkCredential(
+ mLockPatternUtils,
+ LockscreenCredential.createPattern(pattern),
+ userId,
+ new LockPatternChecker.OnCheckCallback() {
+
+ @Override
+ public void onEarlyMatched() {
+ mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL);
+ onPatternChecked(userId, true /* matched */, 0 /* timeoutMs */,
+ true /* isValidPattern */);
+ }
+
+ @Override
+ public void onChecked(boolean matched, int timeoutMs) {
+ mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ mLockPatternView.enableInput();
+ mPendingLockCheck = null;
+ if (!matched) {
+ onPatternChecked(userId, false /* matched */, timeoutMs,
+ true /* isValidPattern */);
+ }
+ }
+
+ @Override
+ public void onCancelled() {
+ // We already got dismissed with the early matched callback, so we
+ // cancelled the check. However, we still need to note down the latency.
+ mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ }
+ });
+ if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
+ getKeyguardSecurityCallback().userActivity();
+ getKeyguardSecurityCallback().onUserInput();
+ }
+ }
+
+ private void onPatternChecked(int userId, boolean matched, int timeoutMs,
+ boolean isValidPattern) {
+ boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
+ if (matched) {
+ getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0);
+ if (dismissKeyguard) {
+ mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
+ getKeyguardSecurityCallback().dismiss(true, userId);
+ }
+ } else {
+ mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
+ if (isValidPattern) {
+ getKeyguardSecurityCallback().reportUnlockAttempt(userId, false, timeoutMs);
+ if (timeoutMs > 0) {
+ long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
+ userId, timeoutMs);
+ handleAttemptLockout(deadline);
+ }
+ }
+ if (timeoutMs == 0) {
+ mMessageAreaController.setMessage(R.string.kg_wrong_pattern);
+ mLockPatternView.postDelayed(mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS);
+ }
+ }
+ }
+ }
+
+ protected KeyguardPatternViewController(KeyguardPatternView view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode,
+ LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ LatencyTracker latencyTracker,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory) {
+ super(view, securityMode, keyguardSecurityCallback);
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mLockPatternUtils = lockPatternUtils;
+ mLatencyTracker = latencyTracker;
+ mMessageAreaControllerFactory = messageAreaControllerFactory;
+ KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
+ mMessageAreaController = mMessageAreaControllerFactory.create(kma);
+ mLockPatternView = mView.findViewById(R.id.lockPatternView);
+ }
+
+ @Override
+ public void init() {
+ super.init();
+ mMessageAreaController.init();
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mLockPatternView.setOnPatternListener(new UnlockPatternListener());
+ mLockPatternView.setSaveEnabled(false);
+ mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
+ KeyguardUpdateMonitor.getCurrentUser()));
+ // vibrate mode will be the same for the life of this screen
+ mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
+
+ EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
+ if (button != null) {
+ button.setCallback(mEmergencyButtonCallback);
+ }
+
+ View cancelBtn = mView.findViewById(R.id.cancel_button);
+ if (cancelBtn != null) {
+ cancelBtn.setOnClickListener(view -> {
+ getKeyguardSecurityCallback().reset();
+ getKeyguardSecurityCallback().onCancelClicked();
+ });
+ }
+ }
+
+ @Override
+ protected void onViewDetached() {
+ super.onViewDetached();
+ mLockPatternView.setOnPatternListener(null);
+ EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
+ if (button != null) {
+ button.setCallback(null);
+ }
+ View cancelBtn = mView.findViewById(R.id.cancel_button);
+ if (cancelBtn != null) {
+ cancelBtn.setOnClickListener(null);
+ }
+ }
+
+ @Override
+ public void reset() {
+ // reset lock pattern
+ mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
+ KeyguardUpdateMonitor.getCurrentUser()));
+ mLockPatternView.enableInput();
+ mLockPatternView.setEnabled(true);
+ mLockPatternView.clearPattern();
+
+ // if the user is currently locked out, enforce it.
+ long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
+ KeyguardUpdateMonitor.getCurrentUser());
+ if (deadline != 0) {
+ handleAttemptLockout(deadline);
+ } else {
+ displayDefaultSecurityMessage();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ if (mCountdownTimer != null) {
+ mCountdownTimer.cancel();
+ mCountdownTimer = null;
+ }
+
+ if (mPendingLockCheck != null) {
+ mPendingLockCheck.cancel(false);
+ mPendingLockCheck = null;
+ }
+ displayDefaultSecurityMessage();
+ }
+
+ @Override
+ public boolean needsInput() {
+ return false;
+ }
+
+ @Override
+ public void showPromptReason(int reason) {
+ /// TODO: move all this logic into the MessageAreaController?
+ switch (reason) {
+ case PROMPT_REASON_RESTART:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_restart_pattern);
+ break;
+ case PROMPT_REASON_TIMEOUT:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+ break;
+ case PROMPT_REASON_DEVICE_ADMIN:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_device_admin);
+ break;
+ case PROMPT_REASON_USER_REQUEST:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_user_request);
+ break;
+ case PROMPT_REASON_PREPARE_FOR_UPDATE:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+ break;
+ case PROMPT_REASON_NONE:
+ break;
+ default:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+ break;
+ }
+ }
+
+ @Override
+ public void showMessage(CharSequence message, ColorStateList colorState) {
+ if (colorState != null) {
+ mMessageAreaController.setNextMessageColor(colorState);
+ }
+ mMessageAreaController.setMessage(message);
+ }
+
+ @Override
+ public void startAppearAnimation() {
+ super.startAppearAnimation();
+ }
+
+ @Override
+ public boolean startDisappearAnimation(Runnable finishRunnable) {
+ return mView.startDisappearAnimation(
+ mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable);
+ }
+
+ private void displayDefaultSecurityMessage() {
+ mMessageAreaController.setMessage("");
+ }
+
+ private void handleAttemptLockout(long elapsedRealtimeDeadline) {
+ mLockPatternView.clearPattern();
+ mLockPatternView.setEnabled(false);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long secondsInFuture = (long) Math.ceil(
+ (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
+ mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ final int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
+ mMessageAreaController.setMessage(mView.getResources().getQuantityString(
+ R.plurals.kg_too_many_failed_attempts_countdown,
+ secondsRemaining, secondsRemaining));
+ }
+
+ @Override
+ public void onFinish() {
+ mLockPatternView.setEnabled(true);
+ displayDefaultSecurityMessage();
+ }
+
+ }.start();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index c7f27cf..7fa43116 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -16,11 +16,17 @@
package com.android.keyguard;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
+
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.KeyEvent;
-import android.view.MotionEvent;
import android.view.View;
import com.android.internal.widget.LockscreenCredential;
@@ -29,22 +35,12 @@
/**
* A Pin based Keyguard input view
*/
-public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
- implements View.OnKeyListener, View.OnTouchListener {
+public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView {
protected PasswordTextView mPasswordEntry;
private View mOkButton;
private View mDeleteButton;
- private View mButton0;
- private View mButton1;
- private View mButton2;
- private View mButton3;
- private View mButton4;
- private View mButton5;
- private View mButton6;
- private View mButton7;
- private View mButton8;
- private View mButton9;
+ private View[] mButtons = new View[10];
public KeyguardPinBasedInputView(Context context) {
this(context, null);
@@ -62,7 +58,6 @@
@Override
protected void resetState() {
- setPasswordEntryEnabled(true);
}
@Override
@@ -86,10 +81,10 @@
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.isConfirmKey(keyCode)) {
- performClick(mOkButton);
+ mOkButton.performClick();
return true;
} else if (keyCode == KeyEvent.KEYCODE_DEL) {
- performClick(mDeleteButton);
+ mDeleteButton.performClick();
return true;
}
if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
@@ -125,42 +120,9 @@
}
}
- private void performClick(View view) {
- view.performClick();
- }
-
private void performNumberClick(int number) {
- switch (number) {
- case 0:
- performClick(mButton0);
- break;
- case 1:
- performClick(mButton1);
- break;
- case 2:
- performClick(mButton2);
- break;
- case 3:
- performClick(mButton3);
- break;
- case 4:
- performClick(mButton4);
- break;
- case 5:
- performClick(mButton5);
- break;
- case 6:
- performClick(mButton6);
- break;
- case 7:
- performClick(mButton7);
- break;
- case 8:
- performClick(mButton8);
- break;
- case 9:
- performClick(mButton9);
- break;
+ if (number >= 0 && number <= 9) {
+ mButtons[number].performClick();
}
}
@@ -177,94 +139,31 @@
@Override
protected void onFinishInflate() {
mPasswordEntry = findViewById(getPasswordTextViewId());
- mPasswordEntry.setOnKeyListener(this);
// Set selected property on so the view can send accessibility events.
mPasswordEntry.setSelected(true);
- mPasswordEntry.setUserActivityListener(new PasswordTextView.UserActivityListener() {
- @Override
- public void onUserActivity() {
- onUserInput();
- }
- });
-
mOkButton = findViewById(R.id.key_enter);
- if (mOkButton != null) {
- mOkButton.setOnTouchListener(this);
- mOkButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mPasswordEntry.isEnabled()) {
- verifyPasswordAndUnlock();
- }
- }
- });
- mOkButton.setOnHoverListener(new LiftToActivateListener(getContext()));
- }
mDeleteButton = findViewById(R.id.delete_button);
mDeleteButton.setVisibility(View.VISIBLE);
- mDeleteButton.setOnTouchListener(this);
- mDeleteButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- // check for time-based lockouts
- if (mPasswordEntry.isEnabled()) {
- mPasswordEntry.deleteLastChar();
- }
- }
- });
- mDeleteButton.setOnLongClickListener(new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- // check for time-based lockouts
- if (mPasswordEntry.isEnabled()) {
- resetPasswordText(true /* animate */, true /* announce */);
- }
- doHapticKeyClick();
- return true;
- }
- });
- mButton0 = findViewById(R.id.key0);
- mButton1 = findViewById(R.id.key1);
- mButton2 = findViewById(R.id.key2);
- mButton3 = findViewById(R.id.key3);
- mButton4 = findViewById(R.id.key4);
- mButton5 = findViewById(R.id.key5);
- mButton6 = findViewById(R.id.key6);
- mButton7 = findViewById(R.id.key7);
- mButton8 = findViewById(R.id.key8);
- mButton9 = findViewById(R.id.key9);
+ mButtons[0] = findViewById(R.id.key0);
+ mButtons[1] = findViewById(R.id.key1);
+ mButtons[2] = findViewById(R.id.key2);
+ mButtons[3] = findViewById(R.id.key3);
+ mButtons[4] = findViewById(R.id.key4);
+ mButtons[5] = findViewById(R.id.key5);
+ mButtons[6] = findViewById(R.id.key6);
+ mButtons[7] = findViewById(R.id.key7);
+ mButtons[8] = findViewById(R.id.key8);
+ mButtons[9] = findViewById(R.id.key9);
mPasswordEntry.requestFocus();
super.onFinishInflate();
}
@Override
- public void onResume(int reason) {
- super.onResume(reason);
- mPasswordEntry.requestFocus();
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- doHapticKeyClick();
- }
- return false;
- }
-
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- return onKeyDown(keyCode, event);
- }
- return false;
- }
-
- @Override
public CharSequence getTitle() {
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_pin_unlock);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
new file mode 100644
index 0000000..4d0ebff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -0,0 +1,113 @@
+/*
+ * 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.keyguard;
+
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnKeyListener;
+import android.view.View.OnTouchListener;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+
+public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinBasedInputView>
+ extends KeyguardAbsKeyInputViewController<T> {
+
+ private final LiftToActivateListener mLiftToActivateListener;
+ protected PasswordTextView mPasswordEntry;
+
+ private final OnKeyListener mOnKeyListener = (v, keyCode, event) -> {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ return mView.onKeyDown(keyCode, event);
+ }
+ return false;
+ };
+
+ private final OnTouchListener mOnTouchListener = (v, event) -> {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mView.doHapticKeyClick();
+ }
+ return false;
+ };
+
+ protected KeyguardPinBasedInputViewController(T view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode,
+ LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ LatencyTracker latencyTracker,
+ LiftToActivateListener liftToActivateListener) {
+ super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
+ messageAreaControllerFactory, latencyTracker);
+ mLiftToActivateListener = liftToActivateListener;
+ mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
+ }
+
+ @Override
+ protected void onViewAttached() {
+ super.onViewAttached();
+
+ mPasswordEntry.setOnKeyListener(mOnKeyListener);
+ mPasswordEntry.setUserActivityListener(this::onUserInput);
+
+ View deleteButton = mView.findViewById(R.id.delete_button);
+ deleteButton.setOnTouchListener(mOnTouchListener);
+ deleteButton.setOnClickListener(v -> {
+ // check for time-based lockouts
+ if (mPasswordEntry.isEnabled()) {
+ mPasswordEntry.deleteLastChar();
+ }
+ });
+ deleteButton.setOnLongClickListener(v -> {
+ // check for time-based lockouts
+ if (mPasswordEntry.isEnabled()) {
+ mView.resetPasswordText(true /* animate */, true /* announce */);
+ }
+ mView.doHapticKeyClick();
+ return true;
+ });
+
+ View okButton = mView.findViewById(R.id.key_enter);
+ if (okButton != null) {
+ okButton.setOnTouchListener(mOnTouchListener);
+ okButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mPasswordEntry.isEnabled()) {
+ verifyPasswordAndUnlock();
+ }
+ }
+ });
+ okButton.setOnHoverListener(mLiftToActivateListener);
+ }
+ }
+
+ @Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ mPasswordEntry.requestFocus();
+ }
+
+ @Override
+ void resetState() {
+ mView.setPasswordEntryEnabled(true);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
new file mode 100644
index 0000000..6769436
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -0,0 +1,66 @@
+/*
+ * 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.keyguard;
+
+import android.view.View;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+
+public class KeyguardPinViewController
+ extends KeyguardPinBasedInputViewController<KeyguardPINView> {
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
+ protected KeyguardPinViewController(KeyguardPINView view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode, LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ LatencyTracker latencyTracker,
+ LiftToActivateListener liftToActivateListener) {
+ super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
+ messageAreaControllerFactory, latencyTracker, liftToActivateListener);
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ }
+
+ @Override
+ protected void onViewAttached() {
+ super.onViewAttached();
+
+ View cancelBtn = mView.findViewById(R.id.cancel_button);
+ if (cancelBtn != null) {
+ cancelBtn.setOnClickListener(view -> {
+ getKeyguardSecurityCallback().reset();
+ getKeyguardSecurityCallback().onCancelClicked();
+ });
+ }
+ }
+
+ @Override
+ void resetState() {
+ super.resetState();
+ mMessageAreaController.setMessage("");
+ }
+
+ @Override
+ public boolean startDisappearAnimation(Runnable finishRunnable) {
+ return mView.startDisappearAnimation(
+ mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 0517227..4115829 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -21,8 +21,6 @@
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
-import static com.android.systemui.DejankUtils.whitelistIpcs;
-
import static java.lang.Integer.max;
import android.animation.Animator;
@@ -30,25 +28,14 @@
import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.AlertDialog;
-import android.app.admin.DevicePolicyManager;
import android.content.Context;
-import android.content.Intent;
-import android.content.res.ColorStateList;
import android.graphics.Insets;
import android.graphics.Rect;
-import android.metrics.LogMaker;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
import android.util.AttributeSet;
-import android.util.Log;
import android.util.MathUtils;
-import android.util.Slog;
import android.util.TypedValue;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
-import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
@@ -63,42 +50,30 @@
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.UiEventLoggerImpl;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.settingslib.utils.ThreadUtils;
-import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
-import com.android.systemui.shared.system.SysUiStatsLog;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.InjectionInflationController;
import java.util.List;
-public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView {
- private static final boolean DEBUG = KeyguardConstants.DEBUG;
- private static final String TAG = "KeyguardSecurityView";
-
- private static final int USER_TYPE_PRIMARY = 1;
- private static final int USER_TYPE_WORK_PROFILE = 2;
- private static final int USER_TYPE_SECONDARY_USER = 3;
+public class KeyguardSecurityContainer extends FrameLayout {
+ static final int USER_TYPE_PRIMARY = 1;
+ static final int USER_TYPE_WORK_PROFILE = 2;
+ static final int USER_TYPE_SECONDARY_USER = 3;
// Bouncer is dismissed due to no security.
- private static final int BOUNCER_DISMISS_NONE_SECURITY = 0;
+ static final int BOUNCER_DISMISS_NONE_SECURITY = 0;
// Bouncer is dismissed due to pin, password or pattern entered.
- private static final int BOUNCER_DISMISS_PASSWORD = 1;
+ static final int BOUNCER_DISMISS_PASSWORD = 1;
// Bouncer is dismissed due to biometric (face, fingerprint or iris) authenticated.
- private static final int BOUNCER_DISMISS_BIOMETRIC = 2;
+ static final int BOUNCER_DISMISS_BIOMETRIC = 2;
// Bouncer is dismissed due to extended access granted.
- private static final int BOUNCER_DISMISS_EXTENDED_ACCESS = 3;
+ static final int BOUNCER_DISMISS_EXTENDED_ACCESS = 3;
// Bouncer is dismissed due to sim card unlock code entered.
- private static final int BOUNCER_DISMISS_SIM = 4;
+ static final int BOUNCER_DISMISS_SIM = 4;
// Make the view move slower than the finger, as if the spring were applying force.
private static final float TOUCH_Y_MULTIPLIER = 0.25f;
@@ -107,36 +82,23 @@
// How much to scale the default slop by, to avoid accidental drags.
private static final float SLOP_SCALE = 4f;
- private static final UiEventLogger sUiEventLogger = new UiEventLoggerImpl();
-
private static final long IME_DISAPPEAR_DURATION_MS = 125;
- private KeyguardSecurityModel mSecurityModel;
- private LockPatternUtils mLockPatternUtils;
-
@VisibleForTesting
KeyguardSecurityViewFlipper mSecurityViewFlipper;
- private boolean mIsVerifyUnlockOnly;
- private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
- private KeyguardSecurityView mCurrentSecurityView;
- private SecurityCallback mSecurityCallback;
private AlertDialog mAlertDialog;
- private InjectionInflationController mInjectionInflationController;
private boolean mSwipeUpToRetry;
- private AdminSecondaryLockScreenController mSecondaryLockScreenController;
private final ViewConfiguration mViewConfiguration;
private final SpringAnimation mSpringAnimation;
private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
- private final KeyguardUpdateMonitor mUpdateMonitor;
- private final KeyguardStateController mKeyguardStateController;
- private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private float mLastTouchY = -1;
private int mActivePointerId = -1;
private boolean mIsDragging;
private float mStartTouchY = -1;
private boolean mDisappearAnimRunning;
+ private SwipeListener mSwipeListener;
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
@@ -188,19 +150,22 @@
// Used to notify the container when something interesting happens.
public interface SecurityCallback {
- public boolean dismiss(boolean authenticated, int targetUserId,
- boolean bypassSecondaryLockScreen);
- public void userActivity();
- public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
+ boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen);
+ void userActivity();
+ void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
/**
* @param strongAuth wheher the user has authenticated with strong authentication like
* pattern, password or PIN but not by trust agents or fingerprint
* @param targetUserId a user that needs to be the foreground user at the finish completion.
*/
- public void finish(boolean strongAuth, int targetUserId);
- public void reset();
- public void onCancelClicked();
+ void finish(boolean strongAuth, int targetUserId);
+ void reset();
+ void onCancelClicked();
+ }
+
+ public interface SwipeListener {
+ void onSwipeUp();
}
@VisibleForTesting
@@ -251,52 +216,24 @@
public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mSecurityModel = Dependency.get(KeyguardSecurityModel.class);
- mLockPatternUtils = new LockPatternUtils(context);
- mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y);
- mInjectionInflationController = new InjectionInflationController(
- SystemUIFactory.getInstance().getSysUIComponent().createViewInstanceCreatorFactory());
mViewConfiguration = ViewConfiguration.get(context);
- mKeyguardStateController = Dependency.get(KeyguardStateController.class);
- mSecondaryLockScreenController = new AdminSecondaryLockScreenController(context, this,
- mUpdateMonitor, mCallback, new Handler(Looper.myLooper()));
}
- public void setSecurityCallback(SecurityCallback callback) {
- mSecurityCallback = callback;
- }
-
- @Override
- public void onResume(int reason) {
- if (mCurrentSecuritySelection != SecurityMode.None) {
- getSecurityView(mCurrentSecuritySelection).onResume(reason);
- }
+ void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
- updateBiometricRetry();
+ updateBiometricRetry(securityMode, faceAuthEnabled);
}
- @Override
public void onPause() {
if (mAlertDialog != null) {
mAlertDialog.dismiss();
mAlertDialog = null;
}
- mSecondaryLockScreenController.hide();
- if (mCurrentSecuritySelection != SecurityMode.None) {
- getSecurityView(mCurrentSecuritySelection).onPause();
- }
mSecurityViewFlipper.setWindowInsetsAnimationCallback(null);
}
@Override
- public void onStartingToHide() {
- if (mCurrentSecuritySelection != SecurityMode.None) {
- getSecurityView(mCurrentSecuritySelection).onStartingToHide();
- }
- }
-
- @Override
public boolean shouldDelayChildPressedState() {
return true;
}
@@ -318,13 +255,12 @@
return false;
}
// Avoid dragging the pattern view
- if (mCurrentSecurityView.disallowInterceptTouch(event)) {
+ if (mSecurityViewFlipper.getSecurityView().disallowInterceptTouch(event)) {
return false;
}
int index = event.findPointerIndex(mActivePointerId);
float touchSlop = mViewConfiguration.getScaledTouchSlop() * SLOP_SCALE;
- if (mCurrentSecurityView != null && index != -1
- && mStartTouchY - event.getY(index) > touchSlop) {
+ if (index != -1 && mStartTouchY - event.getY(index) > touchSlop) {
mIsDragging = true;
return true;
}
@@ -372,31 +308,28 @@
}
if (action == MotionEvent.ACTION_UP) {
if (-getTranslationY() > TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- MIN_DRAG_SIZE, getResources().getDisplayMetrics())
- && !mUpdateMonitor.isFaceDetectionRunning()) {
- mUpdateMonitor.requestFaceAuth();
- mCallback.userActivity();
- showMessage(null, null);
+ MIN_DRAG_SIZE, getResources().getDisplayMetrics())) {
+ if (mSwipeListener != null) {
+ mSwipeListener.onSwipeUp();
+ }
}
}
return true;
}
+ void setSwipeListener(SwipeListener swipeListener) {
+ mSwipeListener = swipeListener;
+ }
+
private void startSpringAnimation(float startVelocity) {
mSpringAnimation
.setStartVelocity(startVelocity)
.animateToFinalPosition(0);
}
- public void startAppearAnimation() {
- if (mCurrentSecuritySelection != SecurityMode.None) {
- getSecurityView(mCurrentSecuritySelection).startAppearAnimation();
- }
- }
-
- public boolean startDisappearAnimation(Runnable onFinishRunnable) {
+ public void startDisappearAnimation(SecurityMode securitySelection) {
mDisappearAnimRunning = true;
- if (mCurrentSecuritySelection == SecurityMode.Password) {
+ if (securitySelection == SecurityMode.Password) {
mSecurityViewFlipper.getWindowInsetsController().controlWindowInsetsAnimation(ime(),
IME_DISAPPEAR_DURATION_MS,
Interpolators.LINEAR, null, new WindowInsetsAnimationControlListener() {
@@ -441,19 +374,13 @@
}
});
}
- if (mCurrentSecuritySelection != SecurityMode.None) {
- return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation(
- onFinishRunnable);
- }
- return false;
}
/**
* Enables/disables swipe up to retry on the bouncer.
*/
- private void updateBiometricRetry() {
- SecurityMode securityMode = getSecurityMode();
- mSwipeUpToRetry = mKeyguardStateController.isFaceAuthEnabled()
+ private void updateBiometricRetry(SecurityMode securityMode, boolean faceAuthEnabled) {
+ mSwipeUpToRetry = faceAuthEnabled
&& securityMode != SecurityMode.SimPin
&& securityMode != SecurityMode.SimPuk
&& securityMode != SecurityMode.None;
@@ -463,53 +390,11 @@
return mSecurityViewFlipper.getTitle();
}
- @VisibleForTesting
- protected KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
- final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
- KeyguardSecurityView view = null;
- final int children = mSecurityViewFlipper.getChildCount();
- for (int child = 0; child < children; child++) {
- if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) {
- view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child));
- break;
- }
- }
- int layoutId = getLayoutIdFor(securityMode);
- if (view == null && layoutId != 0) {
- final LayoutInflater inflater = LayoutInflater.from(mContext);
- if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
- View v = mInjectionInflationController.injectable(inflater)
- .inflate(layoutId, mSecurityViewFlipper, false);
- mSecurityViewFlipper.addView(v);
- updateSecurityView(v);
- view = (KeyguardSecurityView)v;
- view.reset();
- }
-
- return view;
- }
-
- private void updateSecurityView(View view) {
- if (view instanceof KeyguardSecurityView) {
- KeyguardSecurityView ksv = (KeyguardSecurityView) view;
- ksv.setKeyguardCallback(mCallback);
- ksv.setLockPatternUtils(mLockPatternUtils);
- } else {
- Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
- }
- }
@Override
public void onFinishInflate() {
super.onFinishInflate();
mSecurityViewFlipper = findViewById(R.id.view_flipper);
- mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
- }
-
- public void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
- mSecurityModel.setLockPatternUtils(utils);
- mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
}
@Override
@@ -546,11 +431,12 @@
mAlertDialog.show();
}
- private void showTimeoutDialog(int userId, int timeoutMs) {
- int timeoutInSeconds = (int) timeoutMs / 1000;
+ void showTimeoutDialog(int userId, int timeoutMs, LockPatternUtils lockPatternUtils,
+ SecurityMode securityMode) {
+ int timeoutInSeconds = timeoutMs / 1000;
int messageId = 0;
- switch (mSecurityModel.getSecurityMode(userId)) {
+ switch (securityMode) {
case Pattern:
messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
break;
@@ -570,13 +456,13 @@
if (messageId != 0) {
final String message = mContext.getString(messageId,
- mLockPatternUtils.getCurrentFailedPasswordAttempts(userId),
+ lockPatternUtils.getCurrentFailedPasswordAttempts(userId),
timeoutInSeconds);
showDialog(null, message);
}
}
- private void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
+ void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
String message = null;
switch (userType) {
case USER_TYPE_PRIMARY:
@@ -595,7 +481,7 @@
showDialog(null, message);
}
- private void showWipeDialog(int attempts, int userType) {
+ void showWipeDialog(int attempts, int userType) {
String message = null;
switch (userType) {
case USER_TYPE_PRIMARY:
@@ -614,358 +500,8 @@
showDialog(null, message);
}
- private void reportFailedUnlockAttempt(int userId, int timeoutMs) {
- // +1 for this time
- final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1;
-
- if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
-
- final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
- final int failedAttemptsBeforeWipe =
- dpm.getMaximumFailedPasswordsForWipe(null, userId);
-
- final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
- (failedAttemptsBeforeWipe - failedAttempts)
- : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
- if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
- // The user has installed a DevicePolicyManager that requests a user/profile to be wiped
- // N attempts. Once we get below the grace period, we post this dialog every time as a
- // clear warning until the deletion fires.
- // Check which profile has the strictest policy for failed password attempts
- final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId);
- int userType = USER_TYPE_PRIMARY;
- if (expiringUser == userId) {
- // TODO: http://b/23522538
- if (expiringUser != UserHandle.USER_SYSTEM) {
- userType = USER_TYPE_SECONDARY_USER;
- }
- } else if (expiringUser != UserHandle.USER_NULL) {
- userType = USER_TYPE_WORK_PROFILE;
- } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY
- if (remainingBeforeWipe > 0) {
- showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType);
- } else {
- // Too many attempts. The device will be wiped shortly.
- Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!");
- showWipeDialog(failedAttempts, userType);
- }
- }
- mLockPatternUtils.reportFailedPasswordAttempt(userId);
- if (timeoutMs > 0) {
- mLockPatternUtils.reportPasswordLockout(timeoutMs, userId);
- showTimeoutDialog(userId, timeoutMs);
- }
- }
-
- /**
- * Shows the primary security screen for the user. This will be either the multi-selector
- * or the user's security method.
- * @param turningOff true if the device is being turned off
- */
- void showPrimarySecurityScreen(boolean turningOff) {
- SecurityMode securityMode = whitelistIpcs(() -> mSecurityModel.getSecurityMode(
- KeyguardUpdateMonitor.getCurrentUser()));
- if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
- showSecurityScreen(securityMode);
- }
-
- /**
- * Shows the next security screen if there is one.
- * @param authenticated true if the user entered the correct authentication
- * @param targetUserId a user that needs to be the foreground user at the finish (if called)
- * completion.
- * @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary
- * secondary lock screen requirement, if any.
- * @return true if keyguard is done
- */
- boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId,
- boolean bypassSecondaryLockScreen) {
- if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
- boolean finish = false;
- boolean strongAuth = false;
- int eventSubtype = -1;
- BouncerUiEvent uiEvent = BouncerUiEvent.UNKNOWN;
- if (mUpdateMonitor.getUserHasTrust(targetUserId)) {
- finish = true;
- eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_EXTENDED_ACCESS;
- } else if (mUpdateMonitor.getUserUnlockedWithBiometric(targetUserId)) {
- finish = true;
- eventSubtype = BOUNCER_DISMISS_BIOMETRIC;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_BIOMETRIC;
- } else if (SecurityMode.None == mCurrentSecuritySelection) {
- SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
- if (SecurityMode.None == securityMode) {
- finish = true; // no security required
- eventSubtype = BOUNCER_DISMISS_NONE_SECURITY;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_NONE_SECURITY;
- } else {
- showSecurityScreen(securityMode); // switch to the alternate security view
- }
- } else if (authenticated) {
- switch (mCurrentSecuritySelection) {
- case Pattern:
- case Password:
- case PIN:
- strongAuth = true;
- finish = true;
- eventSubtype = BOUNCER_DISMISS_PASSWORD;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_PASSWORD;
- break;
-
- case SimPin:
- case SimPuk:
- // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
- SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
- if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled(
- KeyguardUpdateMonitor.getCurrentUser())) {
- finish = true;
- eventSubtype = BOUNCER_DISMISS_SIM;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_SIM;
- } else {
- showSecurityScreen(securityMode);
- }
- break;
-
- default:
- Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
- showPrimarySecurityScreen(false);
- break;
- }
- }
- // Check for device admin specified additional security measures.
- if (finish && !bypassSecondaryLockScreen) {
- Intent secondaryLockscreenIntent =
- mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId);
- if (secondaryLockscreenIntent != null) {
- mSecondaryLockScreenController.show(secondaryLockscreenIntent);
- return false;
- }
- }
- if (eventSubtype != -1) {
- mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
- .setType(MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype));
- }
- if (uiEvent != BouncerUiEvent.UNKNOWN) {
- sUiEventLogger.log(uiEvent);
- }
- if (finish) {
- mSecurityCallback.finish(strongAuth, targetUserId);
- }
- return finish;
- }
-
- /**
- * Switches to the given security view unless it's already being shown, in which case
- * this is a no-op.
- *
- * @param securityMode
- */
- private void showSecurityScreen(SecurityMode securityMode) {
- if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
-
- if (securityMode == mCurrentSecuritySelection) return;
-
- KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
- KeyguardSecurityView newView = getSecurityView(securityMode);
-
- // Emulate Activity life cycle
- if (oldView != null) {
- oldView.onPause();
- oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
- }
- if (securityMode != SecurityMode.None) {
- newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
- newView.setKeyguardCallback(mCallback);
- }
-
- // Find and show this child.
- final int childCount = mSecurityViewFlipper.getChildCount();
-
- final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
- for (int i = 0; i < childCount; i++) {
- if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) {
- mSecurityViewFlipper.setDisplayedChild(i);
- break;
- }
- }
-
- mCurrentSecuritySelection = securityMode;
- mCurrentSecurityView = newView;
- mSecurityCallback.onSecurityModeChanged(securityMode,
- securityMode != SecurityMode.None && newView.needsInput());
- }
-
- private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
- public void userActivity() {
- if (mSecurityCallback != null) {
- mSecurityCallback.userActivity();
- }
- }
-
- @Override
- public void onUserInput() {
- mUpdateMonitor.cancelFaceAuth();
- }
-
- @Override
- public void dismiss(boolean authenticated, int targetId) {
- dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false);
- }
-
- @Override
- public void dismiss(boolean authenticated, int targetId,
- boolean bypassSecondaryLockScreen) {
- mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen);
- }
-
- public boolean isVerifyUnlockOnly() {
- return mIsVerifyUnlockOnly;
- }
-
- public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
- if (success) {
- SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
- SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS);
- mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
- // Force a garbage collection in an attempt to erase any lockscreen password left in
- // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard
- // dismiss animation janky.
- ThreadUtils.postOnBackgroundThread(() -> {
- try {
- Thread.sleep(5000);
- } catch (InterruptedException ignored) { }
- Runtime.getRuntime().gc();
- });
- } else {
- SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
- SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE);
- KeyguardSecurityContainer.this.reportFailedUnlockAttempt(userId, timeoutMs);
- }
- mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
- .setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE));
- sUiEventLogger.log(success ? BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS
- : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE);
- }
-
- public void reset() {
- mSecurityCallback.reset();
- }
-
- public void onCancelClicked() {
- mSecurityCallback.onCancelClicked();
- }
- };
-
- // The following is used to ignore callbacks from SecurityViews that are no longer current
- // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
- // state for the current security method.
- private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
- @Override
- public void userActivity() { }
- @Override
- public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { }
- @Override
- public boolean isVerifyUnlockOnly() { return false; }
- @Override
- public void dismiss(boolean securityVerified, int targetUserId) { }
- @Override
- public void dismiss(boolean authenticated, int targetId,
- boolean bypassSecondaryLockScreen) { }
- @Override
- public void onUserInput() { }
- @Override
- public void reset() {}
- };
-
- private int getSecurityViewIdForMode(SecurityMode securityMode) {
- switch (securityMode) {
- case Pattern: return R.id.keyguard_pattern_view;
- case PIN: return R.id.keyguard_pin_view;
- case Password: return R.id.keyguard_password_view;
- case SimPin: return R.id.keyguard_sim_pin_view;
- case SimPuk: return R.id.keyguard_sim_puk_view;
- }
- return 0;
- }
-
- @VisibleForTesting
- public int getLayoutIdFor(SecurityMode securityMode) {
- switch (securityMode) {
- case Pattern: return R.layout.keyguard_pattern_view;
- case PIN: return R.layout.keyguard_pin_view;
- case Password: return R.layout.keyguard_password_view;
- case SimPin: return R.layout.keyguard_sim_pin_view;
- case SimPuk: return R.layout.keyguard_sim_puk_view;
- default:
- return 0;
- }
- }
-
- public SecurityMode getSecurityMode() {
- return mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser());
- }
-
- public SecurityMode getCurrentSecurityMode() {
- return mCurrentSecuritySelection;
- }
-
- public KeyguardSecurityView getCurrentSecurityView() {
- return mCurrentSecurityView;
- }
-
- public void verifyUnlock() {
- mIsVerifyUnlockOnly = true;
- showSecurityScreen(getSecurityMode());
- }
-
- public SecurityMode getCurrentSecuritySelection() {
- return mCurrentSecuritySelection;
- }
-
- public void dismiss(boolean authenticated, int targetUserId) {
- mCallback.dismiss(authenticated, targetUserId);
- }
-
- public boolean needsInput() {
- return mSecurityViewFlipper.needsInput();
- }
-
- @Override
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- mSecurityViewFlipper.setKeyguardCallback(callback);
- }
-
- @Override
public void reset() {
- mSecurityViewFlipper.reset();
mDisappearAnimRunning = false;
}
-
- @Override
- public KeyguardSecurityCallback getCallback() {
- return mSecurityViewFlipper.getCallback();
- }
-
- @Override
- public void showPromptReason(int reason) {
- if (mCurrentSecuritySelection != SecurityMode.None) {
- if (reason != PROMPT_REASON_NONE) {
- Log.i(TAG, "Strong auth required, reason: " + reason);
- }
- getSecurityView(mCurrentSecuritySelection).showPromptReason(reason);
- }
- }
-
- public void showMessage(CharSequence message, ColorStateList colorState) {
- if (mCurrentSecuritySelection != SecurityMode.None) {
- getSecurityView(mCurrentSecuritySelection).showMessage(message, colorState);
- }
- }
-
- @Override
- public void showUsabilityHint() {
- mSecurityViewFlipper.showUsabilityHint();
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 17f25bd08ef..64676e5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -16,33 +16,166 @@
package com.android.keyguard;
-import android.content.res.ColorStateList;
+import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_BIOMETRIC;
+import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_EXTENDED_ACCESS;
+import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_NONE_SECURITY;
+import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_PASSWORD;
+import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_SIM;
+import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_PRIMARY;
+import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_SECONDARY_USER;
+import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_WORK_PROFILE;
+import static com.android.systemui.DejankUtils.whitelistIpcs;
+import android.app.admin.DevicePolicyManager;
+import android.content.Intent;
+import android.content.res.ColorStateList;
+import android.metrics.LogMaker;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityContainer.BouncerUiEvent;
import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback;
+import com.android.keyguard.KeyguardSecurityContainer.SwipeListener;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.keyguard.dagger.KeyguardBouncerScope;
+import com.android.settingslib.utils.ThreadUtils;
+import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
/** Controller for {@link KeyguardSecurityContainer} */
-public class KeyguardSecurityContainerController extends ViewController<KeyguardSecurityContainer> {
+@KeyguardBouncerScope
+public class KeyguardSecurityContainerController extends ViewController<KeyguardSecurityContainer>
+ implements KeyguardSecurityView {
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
+ private static final String TAG = "KeyguardSecurityView";
+
+ private final AdminSecondaryLockScreenController mAdminSecondaryLockScreenController;
private final LockPatternUtils mLockPatternUtils;
- private final KeyguardSecurityViewController.Factory mKeyguardSecurityViewControllerFactory;
+ private final KeyguardUpdateMonitor mUpdateMonitor;
+ private final KeyguardSecurityModel mSecurityModel;
+ private final MetricsLogger mMetricsLogger;
+ private final UiEventLogger mUiEventLogger;
+ private final KeyguardStateController mKeyguardStateController;
+ private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
+
+ private SecurityCallback mSecurityCallback;
+ private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid;
+
+ private KeyguardSecurityCallback mKeyguardSecurityCallback = new KeyguardSecurityCallback() {
+ public void userActivity() {
+ if (mSecurityCallback != null) {
+ mSecurityCallback.userActivity();
+ }
+ }
+
+ @Override
+ public void onUserInput() {
+ mUpdateMonitor.cancelFaceAuth();
+ }
+
+ @Override
+ public void dismiss(boolean authenticated, int targetId) {
+ dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false);
+ }
+
+ @Override
+ public void dismiss(boolean authenticated, int targetId,
+ boolean bypassSecondaryLockScreen) {
+ mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen);
+ }
+
+ public boolean isVerifyUnlockOnly() {
+ return false;
+ }
+
+ public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
+ if (success) {
+ SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
+ SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS);
+ mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
+ // Force a garbage collection in an attempt to erase any lockscreen password left in
+ // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard
+ // dismiss animation janky.
+ ThreadUtils.postOnBackgroundThread(() -> {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException ignored) { }
+ Runtime.getRuntime().gc();
+ });
+ } else {
+ SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
+ SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE);
+ reportFailedUnlockAttempt(userId, timeoutMs);
+ }
+ mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
+ .setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE));
+ mUiEventLogger.log(success ? BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS
+ : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE);
+ }
+
+ public void reset() {
+ mSecurityCallback.reset();
+ }
+
+ public void onCancelClicked() {
+ mSecurityCallback.onCancelClicked();
+ }
+ };
+
+
+ private SwipeListener mSwipeListener = new SwipeListener() {
+ @Override
+ public void onSwipeUp() {
+ if (!mUpdateMonitor.isFaceDetectionRunning()) {
+ mUpdateMonitor.requestFaceAuth();
+ mKeyguardSecurityCallback.userActivity();
+ showMessage(null, null);
+ }
+ }
+ };
@Inject
KeyguardSecurityContainerController(KeyguardSecurityContainer view,
+ AdminSecondaryLockScreenController.Factory adminSecondaryLockScreenControllerFactory,
LockPatternUtils lockPatternUtils,
- KeyguardSecurityViewController.Factory keyguardSecurityViewControllerFactory) {
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ KeyguardSecurityModel keyguardSecurityModel,
+ MetricsLogger metricsLogger,
+ UiEventLogger uiEventLogger,
+ KeyguardStateController keyguardStateController,
+ KeyguardSecurityViewFlipperController securityViewFlipperController) {
super(view);
mLockPatternUtils = lockPatternUtils;
- view.setLockPatternUtils(mLockPatternUtils);
- mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory;
+ mUpdateMonitor = keyguardUpdateMonitor;
+ mSecurityModel = keyguardSecurityModel;
+ mMetricsLogger = metricsLogger;
+ mUiEventLogger = uiEventLogger;
+ mKeyguardStateController = keyguardStateController;
+ mSecurityViewFlipperController = securityViewFlipperController;
+ mAdminSecondaryLockScreenController = adminSecondaryLockScreenControllerFactory.create(
+ mKeyguardSecurityCallback);
+ }
+
+ @Override
+ public void init() {
+ super.init();
+ mSecurityViewFlipperController.init();
}
@Override
protected void onViewAttached() {
+ mView.setSwipeListener(mSwipeListener);
}
@Override
@@ -51,68 +184,270 @@
/** */
public void onPause() {
+ mAdminSecondaryLockScreenController.hide();
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ getCurrentSecurityController().onPause();
+ }
mView.onPause();
}
+
+ /**
+ * Shows the primary security screen for the user. This will be either the multi-selector
+ * or the user's security method.
+ * @param turningOff true if the device is being turned off
+ */
public void showPrimarySecurityScreen(boolean turningOff) {
- mView.showPrimarySecurityScreen(turningOff);
+ SecurityMode securityMode = whitelistIpcs(() -> mSecurityModel.getSecurityMode(
+ KeyguardUpdateMonitor.getCurrentUser()));
+ if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
+ showSecurityScreen(securityMode);
}
+ @Override
public void showPromptReason(int reason) {
- mView.showPromptReason(reason);
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ if (reason != PROMPT_REASON_NONE) {
+ Log.i(TAG, "Strong auth required, reason: " + reason);
+ }
+ getCurrentSecurityController().showPromptReason(reason);
+ }
}
public void showMessage(CharSequence message, ColorStateList colorState) {
- mView.showMessage(message, colorState);
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ getCurrentSecurityController().showMessage(message, colorState);
+ }
}
- public SecurityMode getCurrentSecuritySelection() {
- return mView.getCurrentSecuritySelection();
+ public SecurityMode getCurrentSecurityMode() {
+ return mCurrentSecurityMode;
}
public void dismiss(boolean authenticated, int targetUserId) {
- mView.dismiss(authenticated, targetUserId);
+ mKeyguardSecurityCallback.dismiss(authenticated, targetUserId);
}
public void reset() {
mView.reset();
+ mSecurityViewFlipperController.reset();
}
public CharSequence getTitle() {
return mView.getTitle();
}
- public void onResume(int screenOn) {
- mView.onResume(screenOn);
+ @Override
+ public void onResume(int reason) {
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ getCurrentSecurityController().onResume(reason);
+ }
+ mView.onResume(
+ mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()),
+ mKeyguardStateController.isFaceAuthEnabled());
}
public void startAppearAnimation() {
- mView.startAppearAnimation();
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ getCurrentSecurityController().startAppearAnimation();
+ }
}
public boolean startDisappearAnimation(Runnable onFinishRunnable) {
- return mView.startDisappearAnimation(onFinishRunnable);
+ mView.startDisappearAnimation(getCurrentSecurityMode());
+
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ return getCurrentSecurityController().startDisappearAnimation(onFinishRunnable);
+ }
+
+ return false;
}
public void onStartingToHide() {
- mView.onStartingToHide();
+ if (mCurrentSecurityMode != SecurityMode.None) {
+ getCurrentSecurityController().onStartingToHide();
+ }
}
public void setSecurityCallback(SecurityCallback securityCallback) {
- mView.setSecurityCallback(securityCallback);
+ mSecurityCallback = securityCallback;
}
+ /**
+ * Shows the next security screen if there is one.
+ * @param authenticated true if the user entered the correct authentication
+ * @param targetUserId a user that needs to be the foreground user at the finish (if called)
+ * completion.
+ * @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary
+ * secondary lock screen requirement, if any.
+ * @return true if keyguard is done
+ */
public boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId,
boolean bypassSecondaryLockScreen) {
- return mView.showNextSecurityScreenOrFinish(
- authenticated, targetUserId, bypassSecondaryLockScreen);
+
+ if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
+ boolean finish = false;
+ boolean strongAuth = false;
+ int eventSubtype = -1;
+ BouncerUiEvent uiEvent = BouncerUiEvent.UNKNOWN;
+ if (mUpdateMonitor.getUserHasTrust(targetUserId)) {
+ finish = true;
+ eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_EXTENDED_ACCESS;
+ } else if (mUpdateMonitor.getUserUnlockedWithBiometric(targetUserId)) {
+ finish = true;
+ eventSubtype = BOUNCER_DISMISS_BIOMETRIC;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_BIOMETRIC;
+ } else if (SecurityMode.None == getCurrentSecurityMode()) {
+ SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
+ if (SecurityMode.None == securityMode) {
+ finish = true; // no security required
+ eventSubtype = BOUNCER_DISMISS_NONE_SECURITY;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_NONE_SECURITY;
+ } else {
+ showSecurityScreen(securityMode); // switch to the alternate security view
+ }
+ } else if (authenticated) {
+ switch (getCurrentSecurityMode()) {
+ case Pattern:
+ case Password:
+ case PIN:
+ strongAuth = true;
+ finish = true;
+ eventSubtype = BOUNCER_DISMISS_PASSWORD;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_PASSWORD;
+ break;
+
+ case SimPin:
+ case SimPuk:
+ // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
+ SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
+ if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled(
+ KeyguardUpdateMonitor.getCurrentUser())) {
+ finish = true;
+ eventSubtype = BOUNCER_DISMISS_SIM;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_SIM;
+ } else {
+ showSecurityScreen(securityMode);
+ }
+ break;
+
+ default:
+ Log.v(TAG, "Bad security screen " + getCurrentSecurityMode()
+ + ", fail safe");
+ showPrimarySecurityScreen(false);
+ break;
+ }
+ }
+ // Check for device admin specified additional security measures.
+ if (finish && !bypassSecondaryLockScreen) {
+ Intent secondaryLockscreenIntent =
+ mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId);
+ if (secondaryLockscreenIntent != null) {
+ mAdminSecondaryLockScreenController.show(secondaryLockscreenIntent);
+ return false;
+ }
+ }
+ if (eventSubtype != -1) {
+ mMetricsLogger.write(new LogMaker(MetricsProto.MetricsEvent.BOUNCER)
+ .setType(MetricsProto.MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype));
+ }
+ if (uiEvent != BouncerUiEvent.UNKNOWN) {
+ mUiEventLogger.log(uiEvent);
+ }
+ if (finish) {
+ mSecurityCallback.finish(strongAuth, targetUserId);
+ }
+ return finish;
}
public boolean needsInput() {
- return mView.needsInput();
+ return getCurrentSecurityController().needsInput();
}
- public SecurityMode getCurrentSecurityMode() {
- return mView.getCurrentSecurityMode();
+ /**
+ * Switches to the given security view unless it's already being shown, in which case
+ * this is a no-op.
+ *
+ * @param securityMode
+ */
+ @VisibleForTesting
+ void showSecurityScreen(SecurityMode securityMode) {
+ if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
+
+ if (securityMode == SecurityMode.Invalid || securityMode == mCurrentSecurityMode) {
+ return;
+ }
+
+ KeyguardInputViewController<KeyguardInputView> oldView = getCurrentSecurityController();
+
+ // Emulate Activity life cycle
+ if (oldView != null) {
+ oldView.onPause();
+ }
+
+ KeyguardInputViewController<KeyguardInputView> newView = changeSecurityMode(securityMode);
+ if (newView != null) {
+ newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
+ mSecurityViewFlipperController.show(newView);
+ }
+
+ mSecurityCallback.onSecurityModeChanged(
+ securityMode, newView != null && newView.needsInput());
+ }
+
+ public void reportFailedUnlockAttempt(int userId, int timeoutMs) {
+ // +1 for this time
+ final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1;
+
+ if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
+
+ final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
+ final int failedAttemptsBeforeWipe =
+ dpm.getMaximumFailedPasswordsForWipe(null, userId);
+
+ final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0
+ ? (failedAttemptsBeforeWipe - failedAttempts)
+ : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
+ if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
+ // The user has installed a DevicePolicyManager that requests a user/profile to be wiped
+ // N attempts. Once we get below the grace period, we post this dialog every time as a
+ // clear warning until the deletion fires.
+ // Check which profile has the strictest policy for failed password attempts
+ final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId);
+ int userType = USER_TYPE_PRIMARY;
+ if (expiringUser == userId) {
+ // TODO: http://b/23522538
+ if (expiringUser != UserHandle.USER_SYSTEM) {
+ userType = USER_TYPE_SECONDARY_USER;
+ }
+ } else if (expiringUser != UserHandle.USER_NULL) {
+ userType = USER_TYPE_WORK_PROFILE;
+ } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY
+ if (remainingBeforeWipe > 0) {
+ mView.showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType);
+ } else {
+ // Too many attempts. The device will be wiped shortly.
+ Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!");
+ mView.showWipeDialog(failedAttempts, userType);
+ }
+ }
+ mLockPatternUtils.reportFailedPasswordAttempt(userId);
+ if (timeoutMs > 0) {
+ mLockPatternUtils.reportPasswordLockout(timeoutMs, userId);
+ mView.showTimeoutDialog(userId, timeoutMs, mLockPatternUtils,
+ mSecurityModel.getSecurityMode(userId));
+ }
+ }
+
+ private KeyguardInputViewController<KeyguardInputView> getCurrentSecurityController() {
+ return mSecurityViewFlipperController
+ .getSecurityView(mCurrentSecurityMode, mKeyguardSecurityCallback);
+ }
+
+ private KeyguardInputViewController<KeyguardInputView> changeSecurityMode(
+ SecurityMode securityMode) {
+ mCurrentSecurityMode = securityMode;
+ return getCurrentSecurityController();
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
index ac2160e..c77c867 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -18,13 +18,14 @@
import static com.android.systemui.DejankUtils.whitelistIpcs;
import android.app.admin.DevicePolicyManager;
-import android.content.Context;
+import android.content.res.Resources;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Dependency;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import javax.inject.Inject;
@@ -33,7 +34,7 @@
/**
* The different types of security available.
- * @see KeyguardSecurityContainer#showSecurityScreen
+ * @see KeyguardSecurityContainerController#showSecurityScreen
*/
public enum SecurityMode {
Invalid, // NULL state
@@ -45,21 +46,15 @@
SimPuk // Unlock by entering a sim puk
}
- private final Context mContext;
private final boolean mIsPukScreenAvailable;
- private LockPatternUtils mLockPatternUtils;
+ private final LockPatternUtils mLockPatternUtils;
@Inject
- KeyguardSecurityModel(Context context) {
- mContext = context;
- mLockPatternUtils = new LockPatternUtils(context);
- mIsPukScreenAvailable = mContext.getResources().getBoolean(
+ KeyguardSecurityModel(@Main Resources resources, LockPatternUtils lockPatternUtils) {
+ mIsPukScreenAvailable = resources.getBoolean(
com.android.internal.R.bool.config_enable_puk_unlock_screen);
- }
-
- void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
+ mLockPatternUtils = lockPatternUtils;
}
public SecurityMode getSecurityMode(int userId) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
index 43cef3a..ac00e94 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
@@ -18,11 +18,9 @@
import android.content.res.ColorStateList;
import android.view.MotionEvent;
-import com.android.internal.widget.LockPatternUtils;
-
public interface KeyguardSecurityView {
- static public final int SCREEN_ON = 1;
- static public final int VIEW_REVEALED = 2;
+ int SCREEN_ON = 1;
+ int VIEW_REVEALED = 2;
int PROMPT_REASON_NONE = 0;
@@ -63,18 +61,6 @@
int PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT = 7;
/**
- * Interface back to keyguard to tell it when security
- * @param callback
- */
- void setKeyguardCallback(KeyguardSecurityCallback callback);
-
- /**
- * Set {@link LockPatternUtils} object. Useful for providing a mock interface.
- * @param utils
- */
- void setLockPatternUtils(LockPatternUtils utils);
-
- /**
* Reset the view and prepare to take input. This should do things like clearing the
* password or pattern and clear error messages.
*/
@@ -101,12 +87,6 @@
boolean needsInput();
/**
- * Get {@link KeyguardSecurityCallback} for the given object
- * @return KeyguardSecurityCallback
- */
- KeyguardSecurityCallback getCallback();
-
- /**
* Show a string explaining why the security view needs to be solved.
*
* @param reason a flag indicating which string should be shown, see {@link #PROMPT_REASON_NONE}
@@ -123,12 +103,6 @@
void showMessage(CharSequence message, ColorStateList colorState);
/**
- * Instruct the view to show usability hints, if any.
- *
- */
- void showUsabilityHint();
-
- /**
* Starts the animation which should run when the security view appears.
*/
void startAppearAnimation();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java
deleted file mode 100644
index ef9ba19..0000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java
+++ /dev/null
@@ -1,58 +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.keyguard;
-
-import android.view.View;
-
-import com.android.systemui.util.ViewController;
-
-import javax.inject.Inject;
-
-
-/** Controller for a {@link KeyguardSecurityView}. */
-public class KeyguardSecurityViewController extends ViewController<View> {
-
- private final KeyguardSecurityView mView;
-
- private KeyguardSecurityViewController(KeyguardSecurityView view) {
- super((View) view);
- // KeyguardSecurityView isn't actually a View, so we need to track it ourselves.
- mView = view;
- }
-
- @Override
- protected void onViewAttached() {
-
- }
-
- @Override
- protected void onViewDetached() {
-
- }
-
- /** Factory for a {@link KeyguardSecurityViewController}. */
- public static class Factory {
- @Inject
- public Factory() {
- }
-
- /** Create a new {@link KeyguardSecurityViewController}. */
- public KeyguardSecurityViewController create(KeyguardSecurityView view) {
- return new KeyguardSecurityViewController(view);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index 24da3ad..b8439af 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -31,7 +30,6 @@
import android.widget.FrameLayout;
import android.widget.ViewFlipper;
-import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.R;
/**
@@ -39,7 +37,7 @@
* we can emulate {@link android.view.WindowManager.LayoutParams#FLAG_SLIPPERY} within a view
* hierarchy.
*/
-public class KeyguardSecurityViewFlipper extends ViewFlipper implements KeyguardSecurityView {
+public class KeyguardSecurityViewFlipper extends ViewFlipper {
private static final String TAG = "KeyguardSecurityViewFlipper";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
@@ -69,111 +67,16 @@
return result;
}
- KeyguardSecurityView getSecurityView() {
+ KeyguardInputView getSecurityView() {
View child = getChildAt(getDisplayedChild());
- if (child instanceof KeyguardSecurityView) {
- return (KeyguardSecurityView) child;
+ if (child instanceof KeyguardInputView) {
+ return (KeyguardInputView) child;
}
return null;
}
- @Override
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.setKeyguardCallback(callback);
- }
- }
-
- @Override
- public void setLockPatternUtils(LockPatternUtils utils) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.setLockPatternUtils(utils);
- }
- }
-
- @Override
- public void reset() {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.reset();
- }
- }
-
- @Override
- public void onPause() {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.onPause();
- }
- }
-
- @Override
- public void onResume(int reason) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.onResume(reason);
- }
- }
-
- @Override
- public boolean needsInput() {
- KeyguardSecurityView ksv = getSecurityView();
- return (ksv != null) ? ksv.needsInput() : false;
- }
-
- @Override
- public KeyguardSecurityCallback getCallback() {
- KeyguardSecurityView ksv = getSecurityView();
- return (ksv != null) ? ksv.getCallback() : null;
- }
-
- @Override
- public void showPromptReason(int reason) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.showPromptReason(reason);
- }
- }
-
- @Override
- public void showMessage(CharSequence message, ColorStateList colorState) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.showMessage(message, colorState);
- }
- }
-
- @Override
- public void showUsabilityHint() {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.showUsabilityHint();
- }
- }
-
- @Override
- public void startAppearAnimation() {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- ksv.startAppearAnimation();
- }
- }
-
- @Override
- public boolean startDisappearAnimation(Runnable finishRunnable) {
- KeyguardSecurityView ksv = getSecurityView();
- if (ksv != null) {
- return ksv.startDisappearAnimation(finishRunnable);
- } else {
- return false;
- }
- }
-
- @Override
public CharSequence getTitle() {
- KeyguardSecurityView ksv = getSecurityView();
+ KeyguardInputView ksv = getSecurityView();
if (ksv != null) {
return ksv.getTitle();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
new file mode 100644
index 0000000..4953035
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -0,0 +1,148 @@
+/*
+ * 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.keyguard;
+
+import android.util.Log;
+import android.view.LayoutInflater;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.KeyguardInputViewController.Factory;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.keyguard.dagger.KeyguardBouncerScope;
+import com.android.systemui.R;
+import com.android.systemui.util.ViewController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+/**
+ * Controller for a {@link KeyguardSecurityViewFlipper}.
+ */
+@KeyguardBouncerScope
+public class KeyguardSecurityViewFlipperController
+ extends ViewController<KeyguardSecurityViewFlipper> {
+
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
+ private static final String TAG = "KeyguardSecurityView";
+
+ private final List<KeyguardInputViewController<KeyguardInputView>> mChildren =
+ new ArrayList<>();
+ private final LayoutInflater mLayoutInflater;
+ private final Factory mKeyguardSecurityViewControllerFactory;
+
+ @Inject
+ protected KeyguardSecurityViewFlipperController(KeyguardSecurityViewFlipper view,
+ LayoutInflater layoutInflater,
+ KeyguardInputViewController.Factory keyguardSecurityViewControllerFactory) {
+ super(view);
+ mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory;
+ mLayoutInflater = layoutInflater;
+ }
+
+ @Override
+ protected void onViewAttached() {
+
+ }
+
+ @Override
+ protected void onViewDetached() {
+
+ }
+
+ public void reset() {
+ for (KeyguardInputViewController<KeyguardInputView> child : mChildren) {
+ child.reset();
+ }
+ }
+
+ @VisibleForTesting
+ KeyguardInputViewController<KeyguardInputView> getSecurityView(SecurityMode securityMode,
+ KeyguardSecurityCallback keyguardSecurityCallback) {
+ KeyguardInputViewController<KeyguardInputView> childController = null;
+ for (KeyguardInputViewController<KeyguardInputView> child : mChildren) {
+ if (child.getSecurityMode() == securityMode) {
+ childController = child;
+ break;
+ }
+ }
+
+ if (childController == null
+ && securityMode != SecurityMode.None && securityMode != SecurityMode.Invalid) {
+
+ int layoutId = getLayoutIdFor(securityMode);
+ KeyguardInputView view = null;
+ if (layoutId != 0) {
+ if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
+ view = (KeyguardInputView) mLayoutInflater.inflate(
+ layoutId, mView, false);
+ mView.addView(view);
+ childController = mKeyguardSecurityViewControllerFactory.create(
+ view, securityMode, keyguardSecurityCallback);
+ childController.init();
+
+ mChildren.add(childController);
+ }
+ }
+
+ if (childController == null) {
+ childController = new NullKeyguardInputViewController(
+ securityMode, keyguardSecurityCallback);
+ }
+
+ return childController;
+ }
+
+ private int getLayoutIdFor(SecurityMode securityMode) {
+ switch (securityMode) {
+ case Pattern: return com.android.systemui.R.layout.keyguard_pattern_view;
+ case PIN: return com.android.systemui.R.layout.keyguard_pin_view;
+ case Password: return com.android.systemui.R.layout.keyguard_password_view;
+ case SimPin: return com.android.systemui.R.layout.keyguard_sim_pin_view;
+ case SimPuk: return R.layout.keyguard_sim_puk_view;
+ default:
+ return 0;
+ }
+ }
+
+ /** Makes the supplied child visible if it is contained win this view, */
+ public void show(KeyguardInputViewController<KeyguardInputView> childController) {
+ int index = childController.getIndexIn(mView);
+ if (index != -1) {
+ mView.setDisplayedChild(index);
+ }
+ }
+
+ private static class NullKeyguardInputViewController
+ extends KeyguardInputViewController<KeyguardInputView> {
+ protected NullKeyguardInputViewController(SecurityMode securityMode,
+ KeyguardSecurityCallback keyguardSecurityCallback) {
+ super(null, securityMode, keyguardSecurityCallback);
+ }
+
+ @Override
+ public boolean needsInput() {
+ return false;
+ }
+
+ @Override
+ public void onStartingToHide() {
+
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index 1c47aa0..c0f9ce7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -16,66 +16,19 @@
package com.android.keyguard;
-import android.annotation.NonNull;
-import android.app.AlertDialog;
-import android.app.AlertDialog.Builder;
-import android.app.Dialog;
-import android.app.ProgressDialog;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.telephony.PinResult;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
-import android.view.WindowManager;
-import android.widget.ImageView;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
/**
* Displays a PIN pad for unlocking.
*/
public class KeyguardSimPinView extends KeyguardPinBasedInputView {
- private static final String LOG_TAG = "KeyguardSimPinView";
- private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES;
public static final String TAG = "KeyguardSimPinView";
- private ProgressDialog mSimUnlockProgressDialog = null;
- private CheckSimPin mCheckSimPinThread;
-
- // Below flag is set to true during power-up or when a new SIM card inserted on device.
- // When this is true and when SIM card is PIN locked state, on PIN lock screen, message would
- // be displayed to inform user about the number of remaining PIN attempts left.
- private boolean mShowDefaultMessage = true;
- private int mRemainingAttempts = -1;
- private AlertDialog mRemainingAttemptsDialog;
- private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- private ImageView mSimImageView;
-
- KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
- @Override
- public void onSimStateChanged(int subId, int slotId, int simState) {
- if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
- switch(simState) {
- case TelephonyManager.SIM_STATE_READY: {
- mRemainingAttempts = -1;
- resetState();
- break;
- }
- default:
- resetState();
- }
- }
- };
-
public KeyguardSimPinView(Context context) {
this(context, null);
}
@@ -84,81 +37,9 @@
super(context, attrs);
}
- @Override
- public void resetState() {
- super.resetState();
- if (DEBUG) Log.v(TAG, "Resetting state");
- handleSubInfoChangeIfNeeded();
- if (mShowDefaultMessage) {
- showDefaultMessage();
- }
- boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
-
+ public void setEsimLocked(boolean locked) {
KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area);
- esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
- }
-
- private void setLockedSimMessage() {
- boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
- int count = 1;
- TelephonyManager telephonyManager =
- (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
- if (telephonyManager != null) {
- count = telephonyManager.getActiveModemCount();
- }
- Resources rez = getResources();
- String msg;
- TypedArray array = mContext.obtainStyledAttributes(new int[] { R.attr.wallpaperTextColor });
- int color = array.getColor(0, Color.WHITE);
- array.recycle();
- if (count < 2) {
- msg = rez.getString(R.string.kg_sim_pin_instructions);
- } else {
- SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class)
- .getSubscriptionInfoForSubId(mSubId);
- CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash
- msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName);
- if (info != null) {
- color = info.getIconTint();
- }
- }
- if (isEsimLocked) {
- msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
- }
-
- if (mSecurityMessageDisplay != null && getVisibility() == VISIBLE) {
- mSecurityMessageDisplay.setMessage(msg);
- }
- mSimImageView.setImageTintList(ColorStateList.valueOf(color));
- }
-
- private void showDefaultMessage() {
- setLockedSimMessage();
- if (mRemainingAttempts >= 0) {
- return;
- }
-
- // Sending empty PIN here to query the number of remaining PIN attempts
- new CheckSimPin("", mSubId) {
- void onSimCheckResponse(final PinResult result) {
- Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result "
- + result.toString());
- if (result.getAttemptsRemaining() >= 0) {
- mRemainingAttempts = result.getAttemptsRemaining();
- setLockedSimMessage();
- }
- }
- }.start();
- }
-
- private void handleSubInfoChangeIfNeeded() {
- KeyguardUpdateMonitor monitor = Dependency.get(KeyguardUpdateMonitor.class);
- int subId = monitor.getNextSubIdForState(TelephonyManager.SIM_STATE_PIN_REQUIRED);
- if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
- mSubId = subId;
- mShowDefaultMessage = true;
- mRemainingAttempts = -1;
- }
+ esimButton.setVisibility(locked ? View.VISIBLE : View.GONE);
}
@Override
@@ -173,35 +54,6 @@
return 0;
}
- private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
- String displayMessage;
- int msgId;
- if (attemptsRemaining == 0) {
- displayMessage = getContext().getString(R.string.kg_password_wrong_pin_code_pukked);
- } else if (attemptsRemaining > 0) {
- msgId = isDefault ? R.plurals.kg_password_default_pin_message :
- R.plurals.kg_password_wrong_pin_code;
- displayMessage = getContext().getResources()
- .getQuantityString(msgId, attemptsRemaining, attemptsRemaining);
- } else {
- msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed;
- displayMessage = getContext().getString(msgId);
- }
- if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) {
- displayMessage = getResources()
- .getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
- }
- if (DEBUG) Log.d(LOG_TAG, "getPinPasswordErrorMessage:"
- + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
- return displayMessage;
- }
-
- @Override
- protected boolean shouldLockout(long deadline) {
- // SIM PIN doesn't have a timed lockout
- return false;
- }
-
@Override
protected int getPasswordTextViewId() {
return R.id.simPinEntry;
@@ -214,173 +66,6 @@
if (mEcaView instanceof EmergencyCarrierArea) {
((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true);
}
- mSimImageView = findViewById(R.id.keyguard_sim);
- }
-
- @Override
- public void showUsabilityHint() {
-
- }
-
- @Override
- public void onResume(int reason) {
- super.onResume(reason);
- Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback);
- resetState();
- }
-
- @Override
- public void onPause() {
- // dismiss the dialog.
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.dismiss();
- mSimUnlockProgressDialog = null;
- }
- Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mUpdateMonitorCallback);
- }
-
- /**
- * Since the IPC can block, we want to run the request in a separate thread
- * with a callback.
- */
- private abstract class CheckSimPin extends Thread {
- private final String mPin;
- private int mSubId;
-
- protected CheckSimPin(String pin, int subId) {
- mPin = pin;
- mSubId = subId;
- }
-
- abstract void onSimCheckResponse(@NonNull PinResult result);
-
- @Override
- public void run() {
- if (DEBUG) {
- Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")");
- }
- TelephonyManager telephonyManager =
- ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
- .createForSubscriptionId(mSubId);
- final PinResult result = telephonyManager.supplyPinReportPinResult(mPin);
- if (result == null) {
- Log.e(TAG, "Error result for supplyPinReportResult.");
- post(new Runnable() {
- @Override
- public void run() {
- onSimCheckResponse(PinResult.getDefaultFailedResult());
- }
- });
- } else {
- if (DEBUG) {
- Log.v(TAG, "supplyPinReportResult returned: " + result.toString());
- }
- post(new Runnable() {
- @Override
- public void run() {
- onSimCheckResponse(result);
- }
- });
- }
- }
- }
-
- private Dialog getSimUnlockProgressDialog() {
- if (mSimUnlockProgressDialog == null) {
- mSimUnlockProgressDialog = new ProgressDialog(mContext);
- mSimUnlockProgressDialog.setMessage(
- mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
- mSimUnlockProgressDialog.setIndeterminate(true);
- mSimUnlockProgressDialog.setCancelable(false);
- mSimUnlockProgressDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- }
- return mSimUnlockProgressDialog;
- }
-
- private Dialog getSimRemainingAttemptsDialog(int remaining) {
- String msg = getPinPasswordErrorMessage(remaining, false);
- if (mRemainingAttemptsDialog == null) {
- Builder builder = new AlertDialog.Builder(mContext);
- builder.setMessage(msg);
- builder.setCancelable(false);
- builder.setNeutralButton(R.string.ok, null);
- mRemainingAttemptsDialog = builder.create();
- mRemainingAttemptsDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- } else {
- mRemainingAttemptsDialog.setMessage(msg);
- }
- return mRemainingAttemptsDialog;
- }
-
- @Override
- protected void verifyPasswordAndUnlock() {
- String entry = mPasswordEntry.getText();
-
- if (entry.length() < 4) {
- // otherwise, display a message to the user, and don't submit.
- mSecurityMessageDisplay.setMessage(R.string.kg_invalid_sim_pin_hint);
- resetPasswordText(true /* animate */, true /* announce */);
- mCallback.userActivity();
- return;
- }
-
- getSimUnlockProgressDialog().show();
-
- if (mCheckSimPinThread == null) {
- mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) {
- @Override
- void onSimCheckResponse(final PinResult result) {
- post(new Runnable() {
- @Override
- public void run() {
- mRemainingAttempts = result.getAttemptsRemaining();
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.hide();
- }
- resetPasswordText(true /* animate */,
- /* announce */
- result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
- if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
- Dependency.get(KeyguardUpdateMonitor.class)
- .reportSimUnlocked(mSubId);
- mRemainingAttempts = -1;
- mShowDefaultMessage = true;
- if (mCallback != null) {
- mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
- }
- } else {
- mShowDefaultMessage = false;
- if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
- if (result.getAttemptsRemaining() <= 2) {
- // this is getting critical - show dialog
- getSimRemainingAttemptsDialog(
- result.getAttemptsRemaining()).show();
- } else {
- // show message
- mSecurityMessageDisplay.setMessage(
- getPinPasswordErrorMessage(
- result.getAttemptsRemaining(), false));
- }
- } else {
- // "PIN operation failed!" - no idea what this was and no way to
- // find out. :/
- mSecurityMessageDisplay.setMessage(getContext().getString(
- R.string.kg_password_pin_failed));
- }
- if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
- + " CheckSimPin.onSimCheckResponse: " + result
- + " attemptsRemaining=" + result.getAttemptsRemaining());
- }
- mCallback.userActivity();
- mCheckSimPinThread = null;
- }
- });
- }
- };
- mCheckSimPinThread.start();
- }
}
@Override
@@ -389,11 +74,6 @@
}
@Override
- public boolean startDisappearAnimation(Runnable finishRunnable) {
- return false;
- }
-
- @Override
public CharSequence getTitle() {
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_sim_pin_unlock);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
new file mode 100644
index 0000000..cc8bf4f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -0,0 +1,350 @@
+/*
+ * 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.keyguard;
+
+import android.annotation.NonNull;
+import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.telephony.PinResult;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+
+public class KeyguardSimPinViewController
+ extends KeyguardPinBasedInputViewController<KeyguardSimPinView> {
+ public static final String TAG = "KeyguardSimPinView";
+ private static final String LOG_TAG = "KeyguardSimPinView";
+ private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final TelephonyManager mTelephonyManager;
+
+ private ProgressDialog mSimUnlockProgressDialog;
+ private CheckSimPin mCheckSimPinThread;
+ private int mRemainingAttempts;
+ // Below flag is set to true during power-up or when a new SIM card inserted on device.
+ // When this is true and when SIM card is PIN locked state, on PIN lock screen, message would
+ // be displayed to inform user about the number of remaining PIN attempts left.
+ private boolean mShowDefaultMessage;
+ private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private AlertDialog mRemainingAttemptsDialog;
+ private ImageView mSimImageView;
+
+ KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onSimStateChanged(int subId, int slotId, int simState) {
+ if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
+ if (simState == TelephonyManager.SIM_STATE_READY) {
+ mRemainingAttempts = -1;
+ resetState();
+ } else {
+ resetState();
+ }
+ }
+ };
+
+ protected KeyguardSimPinViewController(KeyguardSimPinView view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode, LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ LatencyTracker latencyTracker,
+ LiftToActivateListener liftToActivateListener,
+ TelephonyManager telephonyManager) {
+ super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
+ messageAreaControllerFactory, latencyTracker, liftToActivateListener);
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mTelephonyManager = telephonyManager;
+ mSimImageView = mView.findViewById(R.id.keyguard_sim);
+ }
+
+ @Override
+ protected void onViewAttached() {
+ super.onViewAttached();
+ }
+
+ @Override
+ void resetState() {
+ super.resetState();
+ if (DEBUG) Log.v(TAG, "Resetting state");
+ handleSubInfoChangeIfNeeded();
+ mMessageAreaController.setMessage("");
+ if (mShowDefaultMessage) {
+ showDefaultMessage();
+ }
+
+ mView.setEsimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId));
+ }
+
+ @Override
+ public boolean startDisappearAnimation(Runnable finishRunnable) {
+ return false;
+ }
+
+ @Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
+ mView.resetState();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
+
+ // dismiss the dialog.
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.dismiss();
+ mSimUnlockProgressDialog = null;
+ }
+ }
+
+ @Override
+ protected void verifyPasswordAndUnlock() {
+ String entry = mPasswordEntry.getText();
+
+ if (entry.length() < 4) {
+ // otherwise, display a message to the user, and don't submit.
+ mMessageAreaController.setMessage(
+ com.android.systemui.R.string.kg_invalid_sim_pin_hint);
+ mView.resetPasswordText(true /* animate */, true /* announce */);
+ getKeyguardSecurityCallback().userActivity();
+ return;
+ }
+
+ getSimUnlockProgressDialog().show();
+
+ if (mCheckSimPinThread == null) {
+ mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) {
+ @Override
+ void onSimCheckResponse(final PinResult result) {
+ mView.post(() -> {
+ mRemainingAttempts = result.getAttemptsRemaining();
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.hide();
+ }
+ mView.resetPasswordText(true /* animate */,
+ /* announce */
+ result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
+ if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
+ mKeyguardUpdateMonitor.reportSimUnlocked(mSubId);
+ mRemainingAttempts = -1;
+ mShowDefaultMessage = true;
+ getKeyguardSecurityCallback().dismiss(
+ true, KeyguardUpdateMonitor.getCurrentUser());
+ } else {
+ mShowDefaultMessage = false;
+ if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
+ if (result.getAttemptsRemaining() <= 2) {
+ // this is getting critical - show dialog
+ getSimRemainingAttemptsDialog(
+ result.getAttemptsRemaining()).show();
+ } else {
+ // show message
+ mMessageAreaController.setMessage(
+ getPinPasswordErrorMessage(
+ result.getAttemptsRemaining(), false));
+ }
+ } else {
+ // "PIN operation failed!" - no idea what this was and no way to
+ // find out. :/
+ mMessageAreaController.setMessage(mView.getResources().getString(
+ R.string.kg_password_pin_failed));
+ }
+ if (DEBUG) {
+ Log.d(LOG_TAG, "verifyPasswordAndUnlock "
+ + " CheckSimPin.onSimCheckResponse: " + result
+ + " attemptsRemaining=" + result.getAttemptsRemaining());
+ }
+ }
+ getKeyguardSecurityCallback().userActivity();
+ mCheckSimPinThread = null;
+ });
+ }
+ };
+ mCheckSimPinThread.start();
+ }
+ }
+
+ private Dialog getSimUnlockProgressDialog() {
+ if (mSimUnlockProgressDialog == null) {
+ mSimUnlockProgressDialog = new ProgressDialog(mView.getContext());
+ mSimUnlockProgressDialog.setMessage(
+ mView.getResources().getString(R.string.kg_sim_unlock_progress_dialog_message));
+ mSimUnlockProgressDialog.setIndeterminate(true);
+ mSimUnlockProgressDialog.setCancelable(false);
+ mSimUnlockProgressDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ }
+ return mSimUnlockProgressDialog;
+ }
+
+
+ private Dialog getSimRemainingAttemptsDialog(int remaining) {
+ String msg = getPinPasswordErrorMessage(remaining, false);
+ if (mRemainingAttemptsDialog == null) {
+ Builder builder = new AlertDialog.Builder(mView.getContext());
+ builder.setMessage(msg);
+ builder.setCancelable(false);
+ builder.setNeutralButton(R.string.ok, null);
+ mRemainingAttemptsDialog = builder.create();
+ mRemainingAttemptsDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ } else {
+ mRemainingAttemptsDialog.setMessage(msg);
+ }
+ return mRemainingAttemptsDialog;
+ }
+
+
+ private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
+ String displayMessage;
+ int msgId;
+ if (attemptsRemaining == 0) {
+ displayMessage = mView.getResources().getString(
+ R.string.kg_password_wrong_pin_code_pukked);
+ } else if (attemptsRemaining > 0) {
+ msgId = isDefault ? R.plurals.kg_password_default_pin_message :
+ R.plurals.kg_password_wrong_pin_code;
+ displayMessage = mView.getResources()
+ .getQuantityString(msgId, attemptsRemaining, attemptsRemaining);
+ } else {
+ msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed;
+ displayMessage = mView.getResources().getString(msgId);
+ }
+ if (KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)) {
+ displayMessage = mView.getResources()
+ .getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
+ }
+ if (DEBUG) {
+ Log.d(LOG_TAG, "getPinPasswordErrorMessage: attemptsRemaining="
+ + attemptsRemaining + " displayMessage=" + displayMessage);
+ }
+ return displayMessage;
+ }
+
+ private void showDefaultMessage() {
+ setLockedSimMessage();
+ if (mRemainingAttempts >= 0) {
+ return;
+ }
+
+ // Sending empty PIN here to query the number of remaining PIN attempts
+ new CheckSimPin("", mSubId) {
+ void onSimCheckResponse(final PinResult result) {
+ Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result "
+ + result.toString());
+ if (result.getAttemptsRemaining() >= 0) {
+ mRemainingAttempts = result.getAttemptsRemaining();
+ setLockedSimMessage();
+ }
+ }
+ }.start();
+ }
+
+ /**
+ * Since the IPC can block, we want to run the request in a separate thread
+ * with a callback.
+ */
+ private abstract class CheckSimPin extends Thread {
+ private final String mPin;
+ private int mSubId;
+
+ protected CheckSimPin(String pin, int subId) {
+ mPin = pin;
+ mSubId = subId;
+ }
+
+ abstract void onSimCheckResponse(@NonNull PinResult result);
+
+ @Override
+ public void run() {
+ if (DEBUG) {
+ Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")");
+ }
+ TelephonyManager telephonyManager =
+ mTelephonyManager.createForSubscriptionId(mSubId);
+ final PinResult result = telephonyManager.supplyPinReportPinResult(mPin);
+ if (result == null) {
+ Log.e(TAG, "Error result for supplyPinReportResult.");
+ mView.post(() -> onSimCheckResponse(PinResult.getDefaultFailedResult()));
+ } else {
+ if (DEBUG) {
+ Log.v(TAG, "supplyPinReportResult returned: " + result.toString());
+ }
+ mView.post(() -> onSimCheckResponse(result));
+ }
+ }
+ }
+
+ private void setLockedSimMessage() {
+ boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
+ int count = 1;
+ if (mTelephonyManager != null) {
+ count = mTelephonyManager.getActiveModemCount();
+ }
+ Resources rez = mView.getResources();
+ String msg;
+ TypedArray array = mView.getContext().obtainStyledAttributes(
+ new int[] { R.attr.wallpaperTextColor });
+ int color = array.getColor(0, Color.WHITE);
+ array.recycle();
+ if (count < 2) {
+ msg = rez.getString(R.string.kg_sim_pin_instructions);
+ } else {
+ SubscriptionInfo info = mKeyguardUpdateMonitor.getSubscriptionInfoForSubId(mSubId);
+ CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash
+ msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName);
+ if (info != null) {
+ color = info.getIconTint();
+ }
+ }
+ if (isEsimLocked) {
+ msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
+ }
+
+ if (mView.getVisibility() == View.VISIBLE) {
+ mMessageAreaController.setMessage(msg);
+ }
+ mSimImageView.setImageTintList(ColorStateList.valueOf(color));
+ }
+
+ private void handleSubInfoChangeIfNeeded() {
+ int subId = mKeyguardUpdateMonitor
+ .getNextSubIdForState(TelephonyManager.SIM_STATE_PIN_REQUIRED);
+ if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
+ mSubId = subId;
+ mShowDefaultMessage = true;
+ mRemainingAttempts = -1;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 5148dd7..0d72c93 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -16,27 +16,10 @@
package com.android.keyguard;
-import android.annotation.NonNull;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ProgressDialog;
import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.telephony.PinResult;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
import android.util.AttributeSet;
import android.util.Log;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.ImageView;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -44,48 +27,9 @@
* Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier.
*/
public class KeyguardSimPukView extends KeyguardPinBasedInputView {
- private static final String LOG_TAG = "KeyguardSimPukView";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
public static final String TAG = "KeyguardSimPukView";
- private ProgressDialog mSimUnlockProgressDialog = null;
- private CheckSimPuk mCheckSimPukThread;
-
- // Below flag is set to true during power-up or when a new SIM card inserted on device.
- // When this is true and when SIM card is PUK locked state, on PIN lock screen, message would
- // be displayed to inform user about the number of remaining PUK attempts left.
- private boolean mShowDefaultMessage = true;
- private int mRemainingAttempts = -1;
- private String mPukText;
- private String mPinText;
- private StateMachine mStateMachine = new StateMachine();
- private AlertDialog mRemainingAttemptsDialog;
- private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- private ImageView mSimImageView;
-
- KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
- @Override
- public void onSimStateChanged(int subId, int slotId, int simState) {
- if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
- switch(simState) {
- // If the SIM is unlocked via a key sequence through the emergency dialer, it will
- // move into the READY state and the PUK lock keyguard should be removed.
- case TelephonyManager.SIM_STATE_READY: {
- mRemainingAttempts = -1;
- mShowDefaultMessage = true;
- // mCallback can be null if onSimStateChanged callback is called when keyguard
- // isn't active.
- if (mCallback != null) {
- mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
- }
- break;
- }
- default:
- resetState();
- }
- }
- };
-
public KeyguardSimPukView(Context context) {
this(context, null);
}
@@ -94,136 +38,14 @@
super(context, attrs);
}
- private class StateMachine {
- final int ENTER_PUK = 0;
- final int ENTER_PIN = 1;
- final int CONFIRM_PIN = 2;
- final int DONE = 3;
- private int state = ENTER_PUK;
-
- public void next() {
- int msg = 0;
- if (state == ENTER_PUK) {
- if (checkPuk()) {
- state = ENTER_PIN;
- msg = R.string.kg_puk_enter_pin_hint;
- } else {
- msg = R.string.kg_invalid_sim_puk_hint;
- }
- } else if (state == ENTER_PIN) {
- if (checkPin()) {
- state = CONFIRM_PIN;
- msg = R.string.kg_enter_confirm_pin_hint;
- } else {
- msg = R.string.kg_invalid_sim_pin_hint;
- }
- } else if (state == CONFIRM_PIN) {
- if (confirmPin()) {
- state = DONE;
- msg = R.string.keyguard_sim_unlock_progress_dialog_message;
- updateSim();
- } else {
- state = ENTER_PIN; // try again?
- msg = R.string.kg_invalid_confirm_pin_hint;
- }
- }
- resetPasswordText(true /* animate */, true /* announce */);
- if (msg != 0) {
- mSecurityMessageDisplay.setMessage(msg);
- }
- }
-
-
- void reset() {
- mPinText="";
- mPukText="";
- state = ENTER_PUK;
- handleSubInfoChangeIfNeeded();
- if (mShowDefaultMessage) {
- showDefaultMessage();
- }
- boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
-
- KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area);
- esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
- mPasswordEntry.requestFocus();
- }
-
-
- }
-
- private void showDefaultMessage() {
- if (mRemainingAttempts >= 0) {
- mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage(
- mRemainingAttempts, true));
- return;
- }
-
- boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
- int count = 1;
- TelephonyManager telephonyManager =
- (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
- if (telephonyManager != null) {
- count = telephonyManager.getActiveModemCount();
- }
- Resources rez = getResources();
- String msg;
- TypedArray array = mContext.obtainStyledAttributes(new int[] { R.attr.wallpaperTextColor });
- int color = array.getColor(0, Color.WHITE);
- array.recycle();
- if (count < 2) {
- msg = rez.getString(R.string.kg_puk_enter_puk_hint);
- } else {
- SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class)
- .getSubscriptionInfoForSubId(mSubId);
- CharSequence displayName = info != null ? info.getDisplayName() : "";
- msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName);
- if (info != null) {
- color = info.getIconTint();
- }
- }
- if (isEsimLocked) {
- msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
- }
- if (mSecurityMessageDisplay != null) {
- mSecurityMessageDisplay.setMessage(msg);
- }
- mSimImageView.setImageTintList(ColorStateList.valueOf(color));
-
- // Sending empty PUK here to query the number of remaining PIN attempts
- new CheckSimPuk("", "", mSubId) {
- void onSimLockChangedResponse(final PinResult result) {
- if (result == null) Log.e(LOG_TAG, "onSimCheckResponse, pin result is NULL");
- else {
- Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result "
- + result.toString());
- if (result.getAttemptsRemaining() >= 0) {
- mRemainingAttempts = result.getAttemptsRemaining();
- mSecurityMessageDisplay.setMessage(
- getPukPasswordErrorMessage(result.getAttemptsRemaining(), true));
- }
- }
- }
- }.start();
- }
-
- private void handleSubInfoChangeIfNeeded() {
- KeyguardUpdateMonitor monitor = Dependency.get(KeyguardUpdateMonitor.class);
- int subId = monitor.getNextSubIdForState(TelephonyManager.SIM_STATE_PUK_REQUIRED);
- if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
- mSubId = subId;
- mShowDefaultMessage = true;
- mRemainingAttempts = -1;
- }
- }
-
@Override
protected int getPromptReasonStringRes(int reason) {
// No message on SIM Puk
return 0;
}
- private String getPukPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
+ String getPukPasswordErrorMessage(
+ int attemptsRemaining, boolean isDefault, boolean isEsimLocked) {
String displayMessage;
if (attemptsRemaining == 0) {
@@ -238,28 +60,19 @@
R.string.kg_password_puk_failed;
displayMessage = getContext().getString(msgId);
}
- if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) {
+ if (isEsimLocked) {
displayMessage = getResources()
.getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
}
- if (DEBUG) Log.d(LOG_TAG, "getPukPasswordErrorMessage:"
- + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
+ if (DEBUG) {
+ Log.d(TAG, "getPukPasswordErrorMessage:"
+ + " attemptsRemaining=" + attemptsRemaining
+ + " displayMessage=" + displayMessage);
+ }
return displayMessage;
}
@Override
- public void resetState() {
- super.resetState();
- mStateMachine.reset();
- }
-
- @Override
- protected boolean shouldLockout(long deadline) {
- // SIM PUK doesn't have a timed lockout
- return false;
- }
-
- @Override
protected int getPasswordTextViewId() {
return R.id.pukEntry;
}
@@ -271,197 +84,6 @@
if (mEcaView instanceof EmergencyCarrierArea) {
((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true);
}
- mSimImageView = findViewById(R.id.keyguard_sim);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback);
- resetState();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mUpdateMonitorCallback);
- }
-
- @Override
- public void showUsabilityHint() {
- }
-
- @Override
- public void onPause() {
- // dismiss the dialog.
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.dismiss();
- mSimUnlockProgressDialog = null;
- }
- }
-
- /**
- * Since the IPC can block, we want to run the request in a separate thread
- * with a callback.
- */
- private abstract class CheckSimPuk extends Thread {
-
- private final String mPin, mPuk;
- private final int mSubId;
-
- protected CheckSimPuk(String puk, String pin, int subId) {
- mPuk = puk;
- mPin = pin;
- mSubId = subId;
- }
-
- abstract void onSimLockChangedResponse(@NonNull PinResult result);
-
- @Override
- public void run() {
- if (DEBUG) Log.v(TAG, "call supplyPukReportResult()");
- TelephonyManager telephonyManager =
- ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
- .createForSubscriptionId(mSubId);
- final PinResult result = telephonyManager.supplyPukReportPinResult(mPuk, mPin);
- if (result == null) {
- Log.e(TAG, "Error result for supplyPukReportResult.");
- post(new Runnable() {
- @Override
- public void run() {
- onSimLockChangedResponse(PinResult.getDefaultFailedResult());
- }
- });
- } else {
- if (DEBUG) {
- Log.v(TAG, "supplyPukReportResult returned: " + result.toString());
- }
- post(new Runnable() {
- @Override
- public void run() {
- onSimLockChangedResponse(result);
- }
- });
- }
- }
- }
-
- private Dialog getSimUnlockProgressDialog() {
- if (mSimUnlockProgressDialog == null) {
- mSimUnlockProgressDialog = new ProgressDialog(mContext);
- mSimUnlockProgressDialog.setMessage(
- mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
- mSimUnlockProgressDialog.setIndeterminate(true);
- mSimUnlockProgressDialog.setCancelable(false);
- if (!(mContext instanceof Activity)) {
- mSimUnlockProgressDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- }
- }
- return mSimUnlockProgressDialog;
- }
-
- private Dialog getPukRemainingAttemptsDialog(int remaining) {
- String msg = getPukPasswordErrorMessage(remaining, false);
- if (mRemainingAttemptsDialog == null) {
- AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
- builder.setMessage(msg);
- builder.setCancelable(false);
- builder.setNeutralButton(R.string.ok, null);
- mRemainingAttemptsDialog = builder.create();
- mRemainingAttemptsDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- } else {
- mRemainingAttemptsDialog.setMessage(msg);
- }
- return mRemainingAttemptsDialog;
- }
-
- private boolean checkPuk() {
- // make sure the puk is at least 8 digits long.
- if (mPasswordEntry.getText().length() == 8) {
- mPukText = mPasswordEntry.getText();
- return true;
- }
- return false;
- }
-
- private boolean checkPin() {
- // make sure the PIN is between 4 and 8 digits
- int length = mPasswordEntry.getText().length();
- if (length >= 4 && length <= 8) {
- mPinText = mPasswordEntry.getText();
- return true;
- }
- return false;
- }
-
- public boolean confirmPin() {
- return mPinText.equals(mPasswordEntry.getText());
- }
-
- private void updateSim() {
- getSimUnlockProgressDialog().show();
-
- if (mCheckSimPukThread == null) {
- mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText, mSubId) {
- @Override
- void onSimLockChangedResponse(final PinResult result) {
- post(new Runnable() {
- @Override
- public void run() {
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.hide();
- }
- resetPasswordText(true /* animate */,
- /* announce */
- result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
- if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
- Dependency.get(KeyguardUpdateMonitor.class)
- .reportSimUnlocked(mSubId);
- mRemainingAttempts = -1;
- mShowDefaultMessage = true;
- if (mCallback != null) {
- mCallback.dismiss(true,
- KeyguardUpdateMonitor.getCurrentUser());
- }
- } else {
- mShowDefaultMessage = false;
- if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
- // show message
- mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage(
- result.getAttemptsRemaining(), false));
- if (result.getAttemptsRemaining() <= 2) {
- // this is getting critical - show dialog
- getPukRemainingAttemptsDialog(
- result.getAttemptsRemaining()).show();
- } else {
- // show message
- mSecurityMessageDisplay.setMessage(
- getPukPasswordErrorMessage(
- result.getAttemptsRemaining(), false));
- }
- } else {
- mSecurityMessageDisplay.setMessage(getContext().getString(
- R.string.kg_password_puk_failed));
- }
- if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
- + " UpdateSim.onSimCheckResponse: "
- + " attemptsRemaining=" + result.getAttemptsRemaining());
- }
- mStateMachine.reset();
- mCheckSimPukThread = null;
- }
- });
- }
- };
- mCheckSimPukThread.start();
- }
- }
-
- @Override
- protected void verifyPasswordAndUnlock() {
- mStateMachine.next();
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
new file mode 100644
index 0000000..a873749
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -0,0 +1,413 @@
+/*
+ * 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.keyguard;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.telephony.PinResult;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+
+public class KeyguardSimPukViewController
+ extends KeyguardPinBasedInputViewController<KeyguardSimPukView> {
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
+ public static final String TAG = "KeyguardSimPukView";
+
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final TelephonyManager mTelephonyManager;
+
+ private String mPukText;
+ private String mPinText;
+ private int mRemainingAttempts;
+ // Below flag is set to true during power-up or when a new SIM card inserted on device.
+ // When this is true and when SIM card is PUK locked state, on PIN lock screen, message would
+ // be displayed to inform user about the number of remaining PUK attempts left.
+ private boolean mShowDefaultMessage;
+ private StateMachine mStateMachine = new StateMachine();
+ private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private CheckSimPuk mCheckSimPukThread;
+ private ProgressDialog mSimUnlockProgressDialog;
+
+ KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onSimStateChanged(int subId, int slotId, int simState) {
+ if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
+ // If the SIM is unlocked via a key sequence through the emergency dialer, it will
+ // move into the READY state and the PUK lock keyguard should be removed.
+ if (simState == TelephonyManager.SIM_STATE_READY) {
+ mRemainingAttempts = -1;
+ mShowDefaultMessage = true;
+ getKeyguardSecurityCallback().dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ } else {
+ resetState();
+ }
+ }
+ };
+ private ImageView mSimImageView;
+ private AlertDialog mRemainingAttemptsDialog;
+
+ protected KeyguardSimPukViewController(KeyguardSimPukView view,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ SecurityMode securityMode, LockPatternUtils lockPatternUtils,
+ KeyguardSecurityCallback keyguardSecurityCallback,
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ LatencyTracker latencyTracker,
+ LiftToActivateListener liftToActivateListener,
+ TelephonyManager telephonyManager) {
+ super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
+ messageAreaControllerFactory, latencyTracker, liftToActivateListener);
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mTelephonyManager = telephonyManager;
+ mSimImageView = mView.findViewById(R.id.keyguard_sim);
+ }
+
+ @Override
+ protected void onViewAttached() {
+ super.onViewAttached();
+ mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ super.onViewDetached();
+ mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
+ }
+
+ @Override
+ void resetState() {
+ super.resetState();
+ mStateMachine.reset();
+ }
+
+ @Override
+ protected void verifyPasswordAndUnlock() {
+ mStateMachine.next();
+ }
+
+ private class StateMachine {
+ static final int ENTER_PUK = 0;
+ static final int ENTER_PIN = 1;
+ static final int CONFIRM_PIN = 2;
+ static final int DONE = 3;
+
+ private int mState = ENTER_PUK;
+
+ public void next() {
+ int msg = 0;
+ if (mState == ENTER_PUK) {
+ if (checkPuk()) {
+ mState = ENTER_PIN;
+ msg = com.android.systemui.R.string.kg_puk_enter_pin_hint;
+ } else {
+ msg = com.android.systemui.R.string.kg_invalid_sim_puk_hint;
+ }
+ } else if (mState == ENTER_PIN) {
+ if (checkPin()) {
+ mState = CONFIRM_PIN;
+ msg = com.android.systemui.R.string.kg_enter_confirm_pin_hint;
+ } else {
+ msg = com.android.systemui.R.string.kg_invalid_sim_pin_hint;
+ }
+ } else if (mState == CONFIRM_PIN) {
+ if (confirmPin()) {
+ mState = DONE;
+ msg = com.android.systemui.R.string.keyguard_sim_unlock_progress_dialog_message;
+ updateSim();
+ } else {
+ mState = ENTER_PIN; // try again?
+ msg = com.android.systemui.R.string.kg_invalid_confirm_pin_hint;
+ }
+ }
+ mView.resetPasswordText(true /* animate */, true /* announce */);
+ if (msg != 0) {
+ mMessageAreaController.setMessage(msg);
+ }
+ }
+
+
+ void reset() {
+ mPinText = "";
+ mPukText = "";
+ mState = ENTER_PUK;
+ handleSubInfoChangeIfNeeded();
+ if (mShowDefaultMessage) {
+ showDefaultMessage();
+ }
+ boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
+
+ KeyguardEsimArea esimButton = mView.findViewById(R.id.keyguard_esim_area);
+ esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
+ mPasswordEntry.requestFocus();
+ }
+ }
+
+ private void showDefaultMessage() {
+ if (mRemainingAttempts >= 0) {
+ mMessageAreaController.setMessage(mView.getPukPasswordErrorMessage(
+ mRemainingAttempts, true,
+ KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)));
+ return;
+ }
+
+ boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
+ int count = 1;
+ if (mTelephonyManager != null) {
+ count = mTelephonyManager.getActiveModemCount();
+ }
+ Resources rez = mView.getResources();
+ String msg;
+ TypedArray array = mView.getContext().obtainStyledAttributes(
+ new int[] { R.attr.wallpaperTextColor });
+ int color = array.getColor(0, Color.WHITE);
+ array.recycle();
+ if (count < 2) {
+ msg = rez.getString(R.string.kg_puk_enter_puk_hint);
+ } else {
+ SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class)
+ .getSubscriptionInfoForSubId(mSubId);
+ CharSequence displayName = info != null ? info.getDisplayName() : "";
+ msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName);
+ if (info != null) {
+ color = info.getIconTint();
+ }
+ }
+ if (isEsimLocked) {
+ msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
+ }
+ mMessageAreaController.setMessage(msg);
+ mSimImageView.setImageTintList(ColorStateList.valueOf(color));
+
+ // Sending empty PUK here to query the number of remaining PIN attempts
+ new CheckSimPuk("", "", mSubId) {
+ void onSimLockChangedResponse(final PinResult result) {
+ if (result == null) Log.e(TAG, "onSimCheckResponse, pin result is NULL");
+ else {
+ Log.d(TAG, "onSimCheckResponse " + " empty One result "
+ + result.toString());
+ if (result.getAttemptsRemaining() >= 0) {
+ mRemainingAttempts = result.getAttemptsRemaining();
+ mMessageAreaController.setMessage(
+ mView.getPukPasswordErrorMessage(
+ result.getAttemptsRemaining(), true,
+ KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)));
+ }
+ }
+ }
+ }.start();
+ }
+
+ private boolean checkPuk() {
+ // make sure the puk is at least 8 digits long.
+ if (mPasswordEntry.getText().length() == 8) {
+ mPukText = mPasswordEntry.getText();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean checkPin() {
+ // make sure the PIN is between 4 and 8 digits
+ int length = mPasswordEntry.getText().length();
+ if (length >= 4 && length <= 8) {
+ mPinText = mPasswordEntry.getText();
+ return true;
+ }
+ return false;
+ }
+
+ public boolean confirmPin() {
+ return mPinText.equals(mPasswordEntry.getText());
+ }
+
+
+
+
+ private void updateSim() {
+ getSimUnlockProgressDialog().show();
+
+ if (mCheckSimPukThread == null) {
+ mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText, mSubId) {
+ @Override
+ void onSimLockChangedResponse(final PinResult result) {
+ mView.post(() -> {
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.hide();
+ }
+ mView.resetPasswordText(true /* animate */,
+ /* announce */
+ result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
+ if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
+ mKeyguardUpdateMonitor.reportSimUnlocked(mSubId);
+ mRemainingAttempts = -1;
+ mShowDefaultMessage = true;
+
+ getKeyguardSecurityCallback().dismiss(
+ true, KeyguardUpdateMonitor.getCurrentUser());
+ } else {
+ mShowDefaultMessage = false;
+ if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
+ // show message
+ mMessageAreaController.setMessage(mView.getPukPasswordErrorMessage(
+ result.getAttemptsRemaining(), false,
+ KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)));
+ if (result.getAttemptsRemaining() <= 2) {
+ // this is getting critical - show dialog
+ getPukRemainingAttemptsDialog(
+ result.getAttemptsRemaining()).show();
+ } else {
+ // show message
+ mMessageAreaController.setMessage(
+ mView.getPukPasswordErrorMessage(
+ result.getAttemptsRemaining(), false,
+ KeyguardEsimArea.isEsimLocked(
+ mView.getContext(), mSubId)));
+ }
+ } else {
+ mMessageAreaController.setMessage(mView.getResources().getString(
+ R.string.kg_password_puk_failed));
+ }
+ if (DEBUG) {
+ Log.d(TAG, "verifyPasswordAndUnlock "
+ + " UpdateSim.onSimCheckResponse: "
+ + " attemptsRemaining=" + result.getAttemptsRemaining());
+ }
+ }
+ mStateMachine.reset();
+ mCheckSimPukThread = null;
+ });
+ }
+ };
+ mCheckSimPukThread.start();
+ }
+ }
+
+ @Override
+ protected boolean shouldLockout(long deadline) {
+ // SIM PUK doesn't have a timed lockout
+ return false;
+ }
+
+ private Dialog getSimUnlockProgressDialog() {
+ if (mSimUnlockProgressDialog == null) {
+ mSimUnlockProgressDialog = new ProgressDialog(mView.getContext());
+ mSimUnlockProgressDialog.setMessage(
+ mView.getResources().getString(R.string.kg_sim_unlock_progress_dialog_message));
+ mSimUnlockProgressDialog.setIndeterminate(true);
+ mSimUnlockProgressDialog.setCancelable(false);
+ if (!(mView.getContext() instanceof Activity)) {
+ mSimUnlockProgressDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ }
+ }
+ return mSimUnlockProgressDialog;
+ }
+
+ private void handleSubInfoChangeIfNeeded() {
+ int subId = mKeyguardUpdateMonitor.getNextSubIdForState(
+ TelephonyManager.SIM_STATE_PUK_REQUIRED);
+ if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
+ mSubId = subId;
+ mShowDefaultMessage = true;
+ mRemainingAttempts = -1;
+ }
+ }
+
+
+ private Dialog getPukRemainingAttemptsDialog(int remaining) {
+ String msg = mView.getPukPasswordErrorMessage(remaining, false,
+ KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId));
+ if (mRemainingAttemptsDialog == null) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(mView.getContext());
+ builder.setMessage(msg);
+ builder.setCancelable(false);
+ builder.setNeutralButton(R.string.ok, null);
+ mRemainingAttemptsDialog = builder.create();
+ mRemainingAttemptsDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ } else {
+ mRemainingAttemptsDialog.setMessage(msg);
+ }
+ return mRemainingAttemptsDialog;
+ }
+
+ @Override
+ public void onPause() {
+ // dismiss the dialog.
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.dismiss();
+ mSimUnlockProgressDialog = null;
+ }
+ }
+
+ /**
+ * Since the IPC can block, we want to run the request in a separate thread
+ * with a callback.
+ */
+ private abstract class CheckSimPuk extends Thread {
+
+ private final String mPin, mPuk;
+ private final int mSubId;
+
+ protected CheckSimPuk(String puk, String pin, int subId) {
+ mPuk = puk;
+ mPin = pin;
+ mSubId = subId;
+ }
+
+ abstract void onSimLockChangedResponse(@NonNull PinResult result);
+
+ @Override
+ public void run() {
+ if (DEBUG) Log.v(TAG, "call supplyPukReportResult()");
+ TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(mSubId);
+ final PinResult result = telephonyManager.supplyPukReportPinResult(mPuk, mPin);
+ if (result == null) {
+ Log.e(TAG, "Error result for supplyPukReportResult.");
+ mView.post(() -> onSimLockChangedResponse(PinResult.getDefaultFailedResult()));
+ } else {
+ if (DEBUG) {
+ Log.v(TAG, "supplyPukReportResult returned: " + result.toString());
+ }
+ mView.post(new Runnable() {
+ @Override
+ public void run() {
+ onSimLockChangedResponse(result);
+ }
+ });
+ }
+ }
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java b/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java
index e59602b..425e50e 100644
--- a/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java
+++ b/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java
@@ -16,11 +16,12 @@
package com.android.keyguard;
-import android.content.Context;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
+import javax.inject.Inject;
+
/**
* Hover listener that implements lift-to-activate interaction for
* accessibility. May be added to multiple views.
@@ -31,9 +32,9 @@
private boolean mCachedClickableState;
- public LiftToActivateListener(Context context) {
- mAccessibilityManager = (AccessibilityManager) context.getSystemService(
- Context.ACCESSIBILITY_SERVICE);
+ @Inject
+ LiftToActivateListener(AccessibilityManager accessibilityManager) {
+ mAccessibilityManager = accessibilityManager;
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index b0457fc..2205fdd 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -26,6 +26,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
import android.widget.TextView;
import com.android.internal.widget.LockPatternUtils;
@@ -90,7 +91,8 @@
}
setOnClickListener(mListener);
- setOnHoverListener(new LiftToActivateListener(context));
+ setOnHoverListener(new LiftToActivateListener(
+ (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE)));
mLockPatternUtils = new LockPatternUtils(context);
mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
index b6010c8..8811088 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
@@ -22,6 +22,7 @@
import com.android.keyguard.KeyguardHostView;
import com.android.keyguard.KeyguardMessageArea;
import com.android.keyguard.KeyguardSecurityContainer;
+import com.android.keyguard.KeyguardSecurityViewFlipper;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
@@ -58,7 +59,15 @@
/** */
@Provides
@KeyguardBouncerScope
- static KeyguardSecurityContainer preovidesKeyguardSecurityContainer(KeyguardHostView hostView) {
+ static KeyguardSecurityContainer providesKeyguardSecurityContainer(KeyguardHostView hostView) {
return hostView.findViewById(R.id.keyguard_security_container);
}
+
+ /** */
+ @Provides
+ @KeyguardBouncerScope
+ static KeyguardSecurityViewFlipper providesKeyguardSecurityViewFlipper(
+ KeyguardSecurityContainer containerView) {
+ return containerView.findViewById(R.id.view_flipper);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index ed78c94..832edf7 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -80,7 +80,6 @@
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -280,7 +279,6 @@
@Inject Lazy<VisualStabilityManager> mVisualStabilityManager;
@Inject Lazy<NotificationGutsManager> mNotificationGutsManager;
@Inject Lazy<NotificationMediaManager> mNotificationMediaManager;
- @Inject Lazy<NotificationBlockingHelperManager> mNotificationBlockingHelperManager;
@Inject Lazy<NotificationRemoteInputManager> mNotificationRemoteInputManager;
@Inject Lazy<SmartReplyConstants> mSmartReplyConstants;
@Inject Lazy<NotificationListener> mNotificationListener;
@@ -473,8 +471,6 @@
mNotificationGroupAlertTransferHelper::get);
mProviders.put(NotificationMediaManager.class, mNotificationMediaManager::get);
mProviders.put(NotificationGutsManager.class, mNotificationGutsManager::get);
- mProviders.put(NotificationBlockingHelperManager.class,
- mNotificationBlockingHelperManager::get);
mProviders.put(NotificationRemoteInputManager.class,
mNotificationRemoteInputManager::get);
mProviders.put(SmartReplyConstants.class, mSmartReplyConstants::get);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index f159499..80253b4 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -17,6 +17,7 @@
package com.android.systemui;
import android.content.Context;
+import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.Handler;
import android.util.Log;
@@ -26,6 +27,7 @@
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dagger.WMComponent;
+import com.android.systemui.navigationbar.gestural.BackGestureTfClassifierProvider;
import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider;
import java.util.concurrent.ExecutionException;
@@ -110,12 +112,16 @@
return mSysUIComponent;
}
- /** Returns the list of system UI components that should be started. */
+ /**
+ * Returns the list of system UI components that should be started.
+ */
public String[] getSystemUIServiceComponents(Resources resources) {
return resources.getStringArray(R.array.config_systemUIServiceComponents);
}
- /** Returns the list of system UI components that should be started per user. */
+ /**
+ * Returns the list of system UI components that should be started per user.
+ */
public String[] getSystemUIServiceComponentsPerUser(Resources resources) {
return resources.getStringArray(R.array.config_systemUIServiceComponentsPerUser);
}
@@ -125,9 +131,17 @@
* This method is overridden in vendor specific implementation of Sys UI.
*/
public ScreenshotNotificationSmartActionsProvider
- createScreenshotNotificationSmartActionsProvider(Context context,
- Executor executor,
- Handler uiHandler) {
+ createScreenshotNotificationSmartActionsProvider(
+ Context context, Executor executor, Handler uiHandler) {
return new ScreenshotNotificationSmartActionsProvider();
}
-}
+
+ /**
+ * Creates an instance of BackGestureTfClassifierProvider.
+ * This method is overridden in vendor specific implementation of Sys UI.
+ */
+ public BackGestureTfClassifierProvider createBackGestureTfClassifierProvider(
+ AssetManager am) {
+ return new BackGestureTfClassifierProvider();
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index e10d2be..911bf9e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -24,11 +24,11 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Handler;
-import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Log;
import android.view.SurfaceControl;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
@@ -101,10 +101,10 @@
@MainThread
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
- @Nullable RemoteCallback endCallback) {
+ @Nullable IRemoteMagnificationAnimationCallback callback) {
//TODO: b/144080869 support multi-display.
mWindowMagnificationAnimationController.enableWindowMagnification(scale, centerX, centerY,
- endCallback != null ? () -> endCallback.sendResult(null) : null);
+ callback);
}
@MainThread
@@ -120,10 +120,10 @@
}
@MainThread
- void disableWindowMagnification(int displayId, @Nullable RemoteCallback endCallback) {
+ void disableWindowMagnification(int displayId,
+ @Nullable IRemoteMagnificationAnimationCallback callback) {
//TODO: b/144080869 support multi-display.
- mWindowMagnificationAnimationController.deleteWindowMagnification(
- endCallback != null ? () -> endCallback.sendResult(null) : null);
+ mWindowMagnificationAnimationController.deleteWindowMagnification(callback);
}
@Override
@@ -182,10 +182,10 @@
@Override
public void enableWindowMagnification(int displayId, float scale, float centerX,
- float centerY, RemoteCallback remoteCallback) {
+ float centerY, IRemoteMagnificationAnimationCallback callback) {
mHandler.post(
() -> mWindowMagnification.enableWindowMagnification(displayId, scale, centerX,
- centerY, remoteCallback));
+ centerY, callback));
}
@Override
@@ -194,9 +194,10 @@
}
@Override
- public void disableWindowMagnification(int displayId, RemoteCallback remoteCallback) {
+ public void disableWindowMagnification(int displayId,
+ IRemoteMagnificationAnimationCallback callback) {
mHandler.post(() -> mWindowMagnification.disableWindowMagnification(displayId,
- remoteCallback));
+ callback));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
index 2f2e3ea..24d8388 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
@@ -22,7 +22,9 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
+import android.os.RemoteException;
import android.util.Log;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.animation.AccelerateInterpolator;
import com.android.internal.annotations.VisibleForTesting;
@@ -38,7 +40,7 @@
class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUpdateListener,
Animator.AnimatorListener {
- private static final String TAG = "WindowMagnificationBridge";
+ private static final String TAG = "WindowMagnificationAnimationController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@Retention(RetentionPolicy.SOURCE)
@@ -61,7 +63,7 @@
private final Context mContext;
// Called when the animation is ended successfully without cancelling or mStartSpec and
// mEndSpec are equal.
- private Runnable mAnimationEndCallback;
+ private IRemoteMagnificationAnimationCallback mAnimationCallback;
// The flag to ignore the animation end callback.
private boolean mEndAnimationCanceled = false;
@MagnificationState
@@ -93,14 +95,16 @@
* or {@link Float#NaN} to leave unchanged.
* @param centerY The screen-relative Y coordinate around which to center,
* or {@link Float#NaN} to leave unchanged.
- * @param animationEndCallback Called when the transition is complete or the given arguments
- * are as same as current values.
+ * @param animationCallback Called when the transition is complete, the given arguments
+ * are as same as current values, or the transition is interrupted
+ * due to the new transition request.
*
* @see #onAnimationUpdate(ValueAnimator)
*/
void enableWindowMagnification(float scale, float centerX, float centerY,
- @Nullable Runnable animationEndCallback) {
- mAnimationEndCallback = animationEndCallback;
+ @Nullable IRemoteMagnificationAnimationCallback animationCallback) {
+ sendAnimationCallback(false);
+ mAnimationCallback = animationCallback;
setupEnableAnimationSpecs(scale, centerX, centerY);
if (mEndSpec.equals(mStartSpec)) {
if (mState == STATE_DISABLED) {
@@ -108,7 +112,7 @@
} else if (mState == STATE_ENABLING || mState == STATE_DISABLING) {
mValueAnimator.cancel();
}
- sendCallbackIfNeeded();
+ sendAnimationCallback(true);
setState(STATE_ENABLED);
} else {
if (mState == STATE_DISABLING) {
@@ -160,14 +164,17 @@
* Wraps {@link WindowMagnificationController#deleteWindowMagnification()}} with transition
* animation. If the window magnification is enabling, it runs the animation in reverse.
*
- * @param animationEndCallback Called when the transition is complete or the window
- * magnification is disabled already.
+ * @param animationCallback Called when the transition is complete, the given arguments
+ * are as same as current values, or the transition is interrupted
+ * due to the new transition request.
*/
- void deleteWindowMagnification(@Nullable Runnable animationEndCallback) {
- mAnimationEndCallback = animationEndCallback;
+ void deleteWindowMagnification(
+ @Nullable IRemoteMagnificationAnimationCallback animationCallback) {
+ sendAnimationCallback(false);
+ mAnimationCallback = animationCallback;
if (mState == STATE_DISABLED || mState == STATE_DISABLING) {
if (mState == STATE_DISABLED) {
- sendCallbackIfNeeded();
+ sendAnimationCallback(true);
}
return;
}
@@ -220,7 +227,7 @@
} else {
setState(STATE_ENABLED);
}
- sendCallbackIfNeeded();
+ sendAnimationCallback(true);
}
@Override
@@ -236,10 +243,17 @@
public void onAnimationRepeat(Animator animation) {
}
- private void sendCallbackIfNeeded() {
- if (mAnimationEndCallback != null) {
- mAnimationEndCallback.run();
- mAnimationEndCallback = null;
+ private void sendAnimationCallback(boolean success) {
+ if (mAnimationCallback != null) {
+ try {
+ mAnimationCallback.onResult(success);
+ if (DEBUG) {
+ Log.d(TAG, "sendAnimationCallback success = " + success);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "sendAnimationCallback failed : " + e);
+ }
+ mAnimationCallback = null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 494a0f64..7140956 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -48,6 +48,7 @@
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.R;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -65,7 +66,8 @@
private final Point mDisplaySize = new Point();
private final int mDisplayId;
@Surface.Rotation
- private int mRotation;
+ @VisibleForTesting
+ int mRotation;
private final Rect mMagnificationFrame = new Rect();
private final SurfaceControl.Transaction mTransaction;
@@ -97,6 +99,7 @@
private SurfaceView mMirrorSurfaceView;
private int mMirrorSurfaceMargin;
private int mBorderDragSize;
+ private int mDragViewSize;
private int mOuterBorderSize;
// The boundary of magnification frame.
private final Rect mMagnificationFrameBoundary = new Rect();
@@ -109,7 +112,7 @@
WindowMagnificationController(Context context, @NonNull Handler handler,
SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
- MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction,
+ MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction,
@NonNull WindowMagnifierCallback callback) {
mContext = context;
mHandler = handler;
@@ -168,6 +171,8 @@
R.dimen.magnification_mirror_surface_margin);
mBorderDragSize = mResources.getDimensionPixelSize(
R.dimen.magnification_border_drag_size);
+ mDragViewSize = mResources.getDimensionPixelSize(
+ R.dimen.magnification_drag_view_size);
mOuterBorderSize = mResources.getDimensionPixelSize(
R.dimen.magnification_outer_border_margin);
}
@@ -203,13 +208,12 @@
* @param configDiff a bit mask of the differences between the configurations
*/
void onConfigurationChanged(int configDiff) {
- if (!isWindowVisible()) {
- return;
- }
if ((configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
updateDimensions();
- mWm.removeView(mMirrorView);
- createMirrorWindow();
+ if (isWindowVisible()) {
+ mWm.removeView(mMirrorView);
+ createMirrorWindow();
+ }
} else if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
onRotate();
}
@@ -217,16 +221,20 @@
/** Handles MirrorWindow position when the device rotation changed. */
private void onRotate() {
- Display display = mContext.getDisplay();
+ final Display display = mContext.getDisplay();
+ final int oldRotation = mRotation;
display.getRealSize(mDisplaySize);
setMagnificationFrameBoundary();
+ mRotation = display.getRotation();
+ if (!isWindowVisible()) {
+ return;
+ }
// Keep MirrorWindow position on the screen unchanged when device rotates 90°
// clockwise or anti-clockwise.
- final int rotationDegree = getDegreeFromRotation(display.getRotation(), mRotation);
+ final int rotationDegree = getDegreeFromRotation(mRotation, oldRotation);
final Matrix matrix = new Matrix();
matrix.setRotate(rotationDegree);
- mRotation = display.getRotation();
if (rotationDegree == 90) {
matrix.postTranslate(mDisplaySize.x, 0);
} else if (rotationDegree == 270) {
@@ -307,6 +315,10 @@
Region regionInsideDragBorder = new Region(mBorderDragSize, mBorderDragSize,
mMirrorView.getWidth() - mBorderDragSize,
mMirrorView.getHeight() - mBorderDragSize);
+ Rect dragArea = new Rect(mMirrorView.getWidth() - mDragViewSize - mBorderDragSize,
+ mMirrorView.getHeight() - mDragViewSize - mBorderDragSize,
+ mMirrorView.getWidth(), mMirrorView.getHeight());
+ regionInsideDragBorder.op(dragArea, Region.Op.DIFFERENCE);
return regionInsideDragBorder;
}
@@ -555,6 +567,7 @@
/**
* Gets the scale.
+ *
* @return {@link Float#NaN} if the window is invisible.
*/
float getScale() {
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index a3339f6..0fd4765 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -16,8 +16,13 @@
package com.android.systemui.appops;
+import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED;
+
import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.location.LocationManager;
import android.media.AudioManager;
@@ -34,6 +39,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
@@ -54,7 +60,7 @@
* NotificationPresenter to be displayed to the user.
*/
@SysUISingleton
-public class AppOpsControllerImpl implements AppOpsController,
+public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsController,
AppOpsManager.OnOpActiveChangedInternalListener,
AppOpsManager.OnOpNotedListener, Dumpable {
@@ -65,6 +71,7 @@
private static final String TAG = "AppOpsControllerImpl";
private static final boolean DEBUG = false;
+ private final BroadcastDispatcher mDispatcher;
private final AppOpsManager mAppOps;
private final AudioManager mAudioManager;
private final LocationManager mLocationManager;
@@ -79,6 +86,7 @@
private final SparseArray<Set<Callback>> mCallbacksByCode = new SparseArray<>();
private final PermissionFlagsCache mFlagsCache;
private boolean mListening;
+ private boolean mMicMuted;
@GuardedBy("mActiveItems")
private final List<AppOpItem> mActiveItems = new ArrayList<>();
@@ -105,8 +113,10 @@
@Background Looper bgLooper,
DumpManager dumpManager,
PermissionFlagsCache cache,
- AudioManager audioManager
+ AudioManager audioManager,
+ BroadcastDispatcher dispatcher
) {
+ mDispatcher = dispatcher;
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mFlagsCache = cache;
mBGHandler = new H(bgLooper);
@@ -115,6 +125,7 @@
mCallbacksByCode.put(OPS[i], new ArraySet<>());
}
mAudioManager = audioManager;
+ mMicMuted = audioManager.isMicrophoneMute();
mLocationManager = context.getSystemService(LocationManager.class);
dumpManager.registerDumpable(TAG, this);
}
@@ -133,6 +144,8 @@
mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler);
mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged(
mAudioManager.getActiveRecordingConfigurations()));
+ mDispatcher.registerReceiverWithHandler(this,
+ new IntentFilter(ACTION_MICROPHONE_MUTE_CHANGED), mBGHandler);
} else {
mAppOps.stopWatchingActive(this);
@@ -140,6 +153,7 @@
mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback);
mBGHandler.removeCallbacksAndMessages(null); // null removes all
+ mDispatcher.unregisterReceiver(this);
synchronized (mActiveItems) {
mActiveItems.clear();
mRecordingsByUid.clear();
@@ -468,6 +482,9 @@
}
private boolean isAnyRecordingPausedLocked(int uid) {
+ if (mMicMuted) {
+ return true;
+ }
List<AudioRecordingConfiguration> configs = mRecordingsByUid.get(uid);
if (configs == null) return false;
int configsNum = configs.size();
@@ -522,6 +539,12 @@
}
};
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mMicMuted = mAudioManager.isMicrophoneMute();
+ updateRecordingPausedStatus();
+ }
+
protected class H extends Handler {
H(Looper looper) {
super(looper);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index ea18b11..77afa27 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -22,7 +22,6 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.TaskStackListener;
import android.content.BroadcastReceiver;
@@ -57,6 +56,7 @@
import java.util.List;
import javax.inject.Inject;
+import javax.inject.Provider;
/**
* Receives messages sent from {@link com.android.server.biometrics.BiometricService} and shows the
@@ -71,7 +71,9 @@
private final CommandQueue mCommandQueue;
private final StatusBarStateController mStatusBarStateController;
- private final Injector mInjector;
+ private final IActivityTaskManager mActivityTaskManager;
+ @Nullable private final FingerprintManager mFingerprintManager;
+ private final Provider<UdfpsController> mUdfpsControllerFactory;
// TODO: These should just be saved from onSaveState
private SomeArgs mCurrentDialogArgs;
@@ -83,16 +85,14 @@
@Nullable
private UdfpsController mUdfpsController;
@VisibleForTesting
- IActivityTaskManager mActivityTaskManager;
- @VisibleForTesting
- BiometricTaskStackListener mTaskStackListener;
+ TaskStackListener mTaskStackListener;
@VisibleForTesting
IBiometricSysuiReceiver mReceiver;
- public class BiometricTaskStackListener extends TaskStackListener {
+ private class BiometricTaskStackListener extends TaskStackListener {
@Override
public void onTaskStackChanged() {
- mHandler.post(mTaskStackChangedRunnable);
+ mHandler.post(AuthController.this::handleTaskStackChanged);
}
}
@@ -119,7 +119,7 @@
}
};
- private final Runnable mTaskStackChangedRunnable = () -> {
+ private void handleTaskStackChanged() {
if (mCurrentDialog != null) {
try {
final String clientPackage = mCurrentDialog.getOpPackageName();
@@ -144,7 +144,7 @@
Log.e(TAG, "Remote exception", e);
}
}
- };
+ }
@Override
public void dozeTimeTick() {
@@ -237,6 +237,34 @@
}
}
+ /**
+ * Requests fingerprint scan.
+ *
+ * @param screenX X position of long press
+ * @param screenY Y position of long press
+ */
+ public void onAodInterrupt(int screenX, int screenY) {
+ if (mUdfpsController == null) {
+ return;
+ }
+ mUdfpsController.onAodInterrupt(screenX, screenY);
+ }
+
+ /**
+ * Cancel a fingerprint scan.
+ *
+ * The sensor that triggers an AOD interrupt for fingerprint doesn't give
+ * ACTION_UP/ACTION_CANCEL events, so the scan needs to be cancelled manually. This should be
+ * called when authentication either succeeds or fails. Failing to cancel the scan will leave
+ * the screen in high brightness mode.
+ */
+ private void onCancelAodInterrupt() {
+ if (mUdfpsController == null) {
+ return;
+ }
+ mUdfpsController.onCancelAodInterrupt();
+ }
+
private void sendResultAndCleanUp(@DismissedReason int reason,
@Nullable byte[] credentialAttestation) {
if (mReceiver == null) {
@@ -251,29 +279,18 @@
onDialogDismissed(reason);
}
- public static class Injector {
- IActivityTaskManager getActivityTaskManager() {
- return ActivityTaskManager.getService();
- }
-
- FingerprintManager getFingerprintManager(Context context) {
- return context.getSystemService(FingerprintManager.class);
- }
- }
-
@Inject
public AuthController(Context context, CommandQueue commandQueue,
- StatusBarStateController statusBarStateController) {
- this(context, commandQueue, statusBarStateController, new Injector());
- }
-
- @VisibleForTesting
- AuthController(Context context, CommandQueue commandQueue,
- StatusBarStateController statusBarStateController, Injector injector) {
+ StatusBarStateController statusBarStateController,
+ IActivityTaskManager activityTaskManager,
+ @Nullable FingerprintManager fingerprintManager,
+ Provider<UdfpsController> udfpsControllerFactory) {
super(context);
mCommandQueue = commandQueue;
mStatusBarStateController = statusBarStateController;
- mInjector = injector;
+ mActivityTaskManager = activityTaskManager;
+ mFingerprintManager = fingerprintManager;
+ mUdfpsControllerFactory = udfpsControllerFactory;
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -286,15 +303,13 @@
public void start() {
mCommandQueue.addCallback(this);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- mActivityTaskManager = mInjector.getActivityTaskManager();
- final FingerprintManager fpm = mInjector.getFingerprintManager(mContext);
- if (fpm != null && fpm.isHardwareDetected()) {
+ if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) {
final List<FingerprintSensorProperties> fingerprintSensorProperties =
- fpm.getSensorProperties();
+ mFingerprintManager.getSensorProperties();
for (FingerprintSensorProperties props : fingerprintSensorProperties) {
if (props.sensorType == FingerprintSensorProperties.TYPE_UDFPS) {
- mUdfpsController = new UdfpsController(mContext, mStatusBarStateController);
+ mUdfpsController = mUdfpsControllerFactory.get();
break;
}
}
@@ -341,6 +356,7 @@
@Override
public void onBiometricAuthenticated() {
mCurrentDialog.onAuthenticationSucceeded();
+ onCancelAodInterrupt();
}
@Override
@@ -390,6 +406,7 @@
if (DEBUG) Log.d(TAG, "onBiometricError, hard error: " + errorMessage);
mCurrentDialog.onError(errorMessage);
}
+ onCancelAodInterrupt();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 82fb808..ba78485 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -16,17 +16,16 @@
package com.android.systemui.biometrics;
-import android.annotation.NonNull;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
import android.annotation.SuppressLint;
-import android.content.ContentResolver;
import android.content.Context;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IUdfpsOverlayController;
-import android.os.Handler;
-import android.os.Looper;
import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.Settings;
@@ -38,14 +37,22 @@
import android.view.MotionEvent;
import android.view.WindowManager;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.internal.BrightnessSynchronizer;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.settings.SystemSettings;
import java.io.FileWriter;
import java.io.IOException;
+import javax.inject.Inject;
+
/**
* Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events,
* and coordinates triggering of the high-brightness mode (HBM).
@@ -54,11 +61,12 @@
private static final String TAG = "UdfpsController";
// Gamma approximation for the sRGB color space.
private static final float DISPLAY_GAMMA = 2.2f;
+ private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000;
private final FingerprintManager mFingerprintManager;
private final WindowManager mWindowManager;
- private final ContentResolver mContentResolver;
- private final Handler mHandler;
+ private final SystemSettings mSystemSettings;
+ private final DelayableExecutor mFgExecutor;
private final WindowManager.LayoutParams mLayoutParams;
private final UdfpsView mView;
// Debugfs path to control the high-brightness mode.
@@ -80,6 +88,13 @@
private final float mDefaultBrightness;
private boolean mIsOverlayShowing;
+ // The fingerprint AOD trigger doesn't provide an ACTION_UP/ACTION_CANCEL event to tell us when
+ // to turn off high brightness mode. To get around this limitation, the state of the AOD
+ // interrupt is being tracked and a timeout is used as a last resort to turn off high brightness
+ // mode.
+ private boolean mIsAodInterruptActive;
+ @Nullable private Runnable mCancelAodTimeoutAction;
+
public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
@Override
public void showUdfpsOverlay() {
@@ -126,19 +141,29 @@
}
};
+ @Inject
UdfpsController(@NonNull Context context,
- @NonNull StatusBarStateController statusBarStateController) {
- mFingerprintManager = context.getSystemService(FingerprintManager.class);
- mWindowManager = context.getSystemService(WindowManager.class);
- mContentResolver = context.getContentResolver();
- mHandler = new Handler(Looper.getMainLooper());
+ @Main Resources resources,
+ LayoutInflater inflater,
+ @Nullable FingerprintManager fingerprintManager,
+ PowerManager powerManager,
+ WindowManager windowManager,
+ SystemSettings systemSettings,
+ @NonNull StatusBarStateController statusBarStateController,
+ @Main DelayableExecutor fgExecutor) {
+ // The fingerprint manager is queried for UDFPS before this class is constructed, so the
+ // fingerprint manager should never be null.
+ mFingerprintManager = checkNotNull(fingerprintManager);
+ mWindowManager = windowManager;
+ mSystemSettings = systemSettings;
+ mFgExecutor = fgExecutor;
mLayoutParams = createLayoutParams(context);
- mView = (UdfpsView) LayoutInflater.from(context).inflate(R.layout.udfps_view, null, false);
+ mView = (UdfpsView) inflater.inflate(R.layout.udfps_view, null, false);
- mHbmPath = context.getResources().getString(R.string.udfps_hbm_sysfs_path);
- mHbmEnableCommand = context.getResources().getString(R.string.udfps_hbm_enable_command);
- mHbmDisableCommand = context.getResources().getString(R.string.udfps_hbm_disable_command);
+ mHbmPath = resources.getString(R.string.udfps_hbm_sysfs_path);
+ mHbmEnableCommand = resources.getString(R.string.udfps_hbm_enable_command);
+ mHbmDisableCommand = resources.getString(R.string.udfps_hbm_disable_command);
mHbmSupported = !TextUtils.isEmpty(mHbmPath);
mView.setHbmSupported(mHbmSupported);
@@ -146,11 +171,11 @@
// This range only consists of the minimum and maximum values, which only cover
// non-high-brightness mode.
- float[] nitsRange = toFloatArray(context.getResources().obtainTypedArray(
+ float[] nitsRange = toFloatArray(resources.obtainTypedArray(
com.android.internal.R.array.config_screenBrightnessNits));
// The last value of this range corresponds to the high-brightness mode.
- float[] nitsAutoBrightnessValues = toFloatArray(context.getResources().obtainTypedArray(
+ float[] nitsAutoBrightnessValues = toFloatArray(resources.obtainTypedArray(
com.android.internal.R.array.config_autoBrightnessDisplayValuesNits));
mHbmNits = nitsAutoBrightnessValues[nitsAutoBrightnessValues.length - 1];
@@ -159,12 +184,12 @@
// This range only consists of the minimum and maximum backlight values, which only apply
// in non-high-brightness mode.
float[] normalizedBacklightRange = normalizeBacklightRange(
- context.getResources().getIntArray(
+ resources.getIntArray(
com.android.internal.R.array.config_screenBrightnessBacklight));
mBacklightToNitsSpline = Spline.createSpline(normalizedBacklightRange, nitsRange);
mNitsToHbmBacklightSpline = Spline.createSpline(hbmNitsRange, normalizedBacklightRange);
- mDefaultBrightness = obtainDefaultBrightness(context);
+ mDefaultBrightness = obtainDefaultBrightness(powerManager);
// TODO(b/160025856): move to the "dump" method.
Log.v(TAG, String.format("ctor | mNitsRange: [%f, %f]", nitsRange[0], nitsRange[1]));
@@ -183,7 +208,7 @@
}
private void showUdfpsOverlay() {
- mHandler.post(() -> {
+ mFgExecutor.execute(() -> {
if (!mIsOverlayShowing) {
try {
Log.v(TAG, "showUdfpsOverlay | adding window");
@@ -200,7 +225,7 @@
}
private void hideUdfpsOverlay() {
- mHandler.post(() -> {
+ mFgExecutor.execute(() -> {
if (mIsOverlayShowing) {
Log.v(TAG, "hideUdfpsOverlay | removing window");
mView.setOnTouchListener(null);
@@ -217,7 +242,7 @@
// Returns a value in the range of [0, 255].
private int computeScrimOpacity() {
// Backlight setting can be NaN, -1.0f, and [0.0f, 1.0f].
- float backlightSetting = Settings.System.getFloatForUser(mContentResolver,
+ float backlightSetting = mSystemSettings.getFloatForUser(
Settings.System.SCREEN_BRIGHTNESS_FLOAT, mDefaultBrightness,
UserHandle.USER_CURRENT);
@@ -240,6 +265,44 @@
return BrightnessSynchronizer.brightnessFloatToInt(scrimOpacity);
}
+ /**
+ * Request fingerprint scan.
+ *
+ * This is intented to be called in response to a sensor that triggers an AOD interrupt for the
+ * fingerprint sensor.
+ */
+ void onAodInterrupt(int screenX, int screenY) {
+ if (mIsAodInterruptActive) {
+ return;
+ }
+ mIsAodInterruptActive = true;
+ // Since the sensor that triggers the AOD interrupt doesn't provide ACTION_UP/ACTION_CANCEL,
+ // we need to be careful about not letting the screen accidentally remain in high brightness
+ // mode. As a mitigation, queue a call to cancel the fingerprint scan.
+ mCancelAodTimeoutAction = mFgExecutor.executeDelayed(this::onCancelAodInterrupt,
+ AOD_INTERRUPT_TIMEOUT_MILLIS);
+ // using a hard-coded value for major and minor until it is available from the sensor
+ onFingerDown(screenX, screenY, 13.0f, 13.0f);
+ }
+
+ /**
+ * Cancel fingerprint scan.
+ *
+ * This is intented to be called after the fingerprint scan triggered by the AOD interrupt
+ * either succeeds or fails.
+ */
+ void onCancelAodInterrupt() {
+ if (!mIsAodInterruptActive) {
+ return;
+ }
+ if (mCancelAodTimeoutAction != null) {
+ mCancelAodTimeoutAction.run();
+ mCancelAodTimeoutAction = null;
+ }
+ mIsAodInterruptActive = false;
+ onFingerUp();
+ }
+
private void onFingerDown(int x, int y, float minor, float major) {
mView.setScrimAlpha(computeScrimOpacity());
mView.showScrimAndDot();
@@ -293,8 +356,7 @@
return lp;
}
- private static float obtainDefaultBrightness(Context context) {
- PowerManager powerManager = context.getSystemService(PowerManager.class);
+ private static float obtainDefaultBrightness(PowerManager powerManager) {
if (powerManager == null) {
Log.e(TAG, "PowerManager is unavailable. Can't obtain default brightness.");
return 0f;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 99875f8..b610602 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -458,6 +458,8 @@
mSavedBubbleKeysPerUser = new SparseSetArray<>();
mCurrentUserId = mNotifUserManager.getCurrentUserId();
+ mBubbleData.setCurrentUserId(mCurrentUserId);
+
mNotifUserManager.addUserChangedListener(
new NotificationLockscreenUserManager.UserChangedListener() {
@Override
@@ -466,6 +468,7 @@
mBubbleData.dismissAll(DISMISS_USER_CHANGED);
BubbleController.this.restoreBubbles(newUserId);
mCurrentUserId = newUserId;
+ mBubbleData.setCurrentUserId(newUserId);
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index a747db6..bab18ec 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -35,6 +35,7 @@
import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController.DismissReason;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -61,6 +62,8 @@
private BubbleLoggerImpl mLogger = new BubbleLoggerImpl();
+ private int mCurrentUserId;
+
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleData" : TAG_BUBBLES;
private static final Comparator<Bubble> BUBBLES_BY_SORT_KEY_DESCENDING =
@@ -617,6 +620,10 @@
mStateChange.selectionChanged = true;
}
+ void setCurrentUserId(int uid) {
+ mCurrentUserId = uid;
+ }
+
/**
* Logs the bubble UI event.
*
@@ -634,7 +641,9 @@
if (provider == null) {
mLogger.logStackUiChanged(packageName, action, bubbleCount, normalX, normalY);
} else if (provider.getKey().equals(BubbleOverflow.KEY)) {
- mLogger.logShowOverflow(packageName, action, bubbleCount, normalX, normalY);
+ if (action == SysUiStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED) {
+ mLogger.logShowOverflow(packageName, mCurrentUserId);
+ }
} else {
mLogger.logBubbleUiChanged((Bubble) provider, packageName, action, bubbleCount, normalX,
normalY, bubbleIndex);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 3331096..f98bb33 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -303,9 +303,11 @@
R.dimen.bubble_manage_button_height);
mSettingsIcon = findViewById(R.id.settings_button);
- mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
- true /* singleTaskInstance */, false /* usePublicVirtualDisplay*/,
- true /* disableSurfaceViewBackgroundLayer */, true /* useTrustedDisplay */);
+ mActivityView = new ActivityView.Builder(mContext)
+ .setSingleInstance(true)
+ .setDisableSurfaceViewBackgroundLayer(true)
+ .setUseTrustedDisplay(true)
+ .build();
// Set ActivityView's alpha value as zero, since there is no view content to be shown.
setContentVisibility(false);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java
index 1e6eb8c..86ba8c5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java
@@ -53,7 +53,10 @@
BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK(489),
@UiEvent(doc = "User blocked notification from bubbling, remove bubble from overflow.")
- BUBBLE_OVERFLOW_REMOVE_BLOCKED(490);
+ BUBBLE_OVERFLOW_REMOVE_BLOCKED(490),
+
+ @UiEvent(doc = "User selected the overflow.")
+ BUBBLE_OVERFLOW_SELECTED(600);
private final int mId;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
index d702cc4..ea612af 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
@@ -16,6 +16,8 @@
package com.android.systemui.bubbles;
+import android.os.UserHandle;
+
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.shared.system.SysUiStatsLog;
@@ -31,10 +33,6 @@
* @param e UI event
*/
public void log(Bubble b, UiEventEnum e) {
- if (b.getInstanceId() == null) {
- // Added from persistence -- TODO log this with specific event?
- return;
- }
logWithInstanceId(e, b.getAppUid(), b.getPackageName(), b.getInstanceId());
}
@@ -82,20 +80,9 @@
false /* isAppForeground (unused) */);
}
- void logShowOverflow(String packageName, int action, int bubbleCount, float normalX,
- float normalY) {
- SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
- packageName,
- BubbleOverflow.KEY /* notification channel */,
- 0 /* notification ID */,
- 0 /* bubble position */,
- bubbleCount,
- action,
- normalX,
- normalY,
- false /* unread bubble */,
- false /* on-going bubble */,
- false /* isAppForeground (unused) */);
+ void logShowOverflow(String packageName, int currentUserId) {
+ super.log(BubbleLogger.Event.BUBBLE_OVERFLOW_SELECTED, currentUserId,
+ packageName);
}
void logBubbleUiChanged(Bubble bubble, String packageName, int action, int bubbleCount,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
index 9ec1452..5b11627 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
@@ -49,7 +49,7 @@
private const val EXTRA_USE_PANEL = "controls.DISPLAY_IN_PANEL"
}
- var activityView = ActivityView(context, null, 0, false)
+ var activityView = ActivityView(context)
val stateCallback: ActivityView.StateCallback = object : ActivityView.StateCallback() {
override fun onActivityViewReady(view: ActivityView) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
index 6e8d63b..25d02a6 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
@@ -18,6 +18,7 @@
import android.content.BroadcastReceiver;
+import com.android.systemui.media.dialog.MediaOutputDialogReceiver;
import com.android.systemui.screenshot.ActionProxyReceiver;
import com.android.systemui.screenshot.DeleteScreenshotReceiver;
import com.android.systemui.screenshot.SmartActionsReceiver;
@@ -59,4 +60,13 @@
public abstract BroadcastReceiver bindSmartActionsReceiver(
SmartActionsReceiver broadcastReceiver);
+ /**
+ *
+ */
+ @Binds
+ @IntoMap
+ @ClassKey(MediaOutputDialogReceiver.class)
+ public abstract BroadcastReceiver bindMediaOutputDialogReceiver(
+ MediaOutputDialogReceiver broadcastReceiver);
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 38e12a6..eb43127 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -288,6 +288,7 @@
/** */
@Provides
+ @SysUISingleton
public LockPatternUtils provideLockPatternUtils(Context context) {
return new LockPatternUtils(context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 66063a8..79925ba 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -41,6 +41,7 @@
import android.hardware.display.ColorDisplayManager;
import android.hardware.display.DisplayManager;
import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
import android.media.AudioManager;
import android.media.MediaRouter2Manager;
import android.media.session.MediaSessionManager;
@@ -61,6 +62,7 @@
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
+import android.view.inputmethod.InputMethodManager;
import com.android.internal.app.IBatteryStats;
import com.android.internal.statusbar.IStatusBarService;
@@ -175,6 +177,19 @@
@Provides
@Singleton
+ @Nullable
+ static FingerprintManager providesFingerprintManager(Context context) {
+ return context.getSystemService(FingerprintManager.class);
+ }
+
+ @Provides
+ @Singleton
+ static InputMethodManager provideInputMethodManager(Context context) {
+ return context.getSystemService(InputMethodManager.class);
+ }
+
+ @Provides
+ @Singleton
static IPackageManager provideIPackageManager() {
return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 3a5ce4d..1f6288a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -35,6 +35,7 @@
import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.notification.InstantAppNotifier;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.tv.TvNotificationPanel;
import com.android.systemui.statusbar.tv.TvStatusBar;
import com.android.systemui.theme.ThemeOverlayController;
import com.android.systemui.toast.ToastUI;
@@ -156,6 +157,12 @@
@ClassKey(TvStatusBar.class)
public abstract SystemUI bindsTvStatusBar(TvStatusBar sysui);
+ /** Inject into TvNotificationPanel. */
+ @Binds
+ @IntoMap
+ @ClassKey(TvNotificationPanel.class)
+ public abstract SystemUI bindsTvNotificationPanel(TvNotificationPanel sysui);
+
/** Inject into VolumeUI. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 99d2651..424a824 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -339,6 +339,7 @@
case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen";
case REASON_SENSOR_WAKE_UP: return "wakeup";
case REASON_SENSOR_TAP: return "tap";
+ case REASON_SENSOR_UDFPS_LONG_PRESS: return "udfps";
default: throw new IllegalArgumentException("invalid reason: " + pulseReason);
}
}
@@ -347,7 +348,8 @@
@IntDef({PULSE_REASON_NONE, PULSE_REASON_INTENT, PULSE_REASON_NOTIFICATION,
PULSE_REASON_SENSOR_SIGMOTION, REASON_SENSOR_PICKUP, REASON_SENSOR_DOUBLE_TAP,
PULSE_REASON_SENSOR_LONG_PRESS, PULSE_REASON_DOCKING, REASON_SENSOR_WAKE_UP,
- PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, REASON_SENSOR_TAP})
+ PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, REASON_SENSOR_TAP,
+ REASON_SENSOR_UDFPS_LONG_PRESS})
public @interface Reason {}
public static final int PULSE_REASON_NONE = -1;
public static final int PULSE_REASON_INTENT = 0;
@@ -360,6 +362,7 @@
public static final int REASON_SENSOR_WAKE_UP = 7;
public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8;
public static final int REASON_SENSOR_TAP = 9;
+ public static final int REASON_SENSOR_UDFPS_LONG_PRESS = 10;
- public static final int TOTAL_REASONS = 10;
+ public static final int TOTAL_REASONS = 11;
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 1ef806c..d5820f3 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -432,8 +432,12 @@
/** Give the Part a chance to clean itself up. */
default void destroy() {}
- /** Alerts that the screenstate is being changed. */
- default void onScreenState(int state) {}
+ /**
+ * Alerts that the screenstate is being changed.
+ * Note: This may be called from within a call to transitionTo, so local DozeState may not
+ * be accurate nor match with the new displayState.
+ */
+ default void onScreenState(int displayState) {}
/** Sets the {@link DozeMachine} when this Part is associated with one. */
default void setDozeMachine(DozeMachine dozeMachine) {}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index d9f9717..5aeb8df 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -30,13 +30,13 @@
import android.provider.Settings;
import android.view.Display;
-import androidx.annotation.Nullable;
-
import com.android.systemui.doze.dagger.BrightnessSensor;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.doze.dagger.WrappedService;
import com.android.systemui.util.sensors.AsyncSensorManager;
+import java.util.Optional;
+
import javax.inject.Inject;
/**
@@ -56,7 +56,7 @@
private final DozeHost mDozeHost;
private final Handler mHandler;
private final SensorManager mSensorManager;
- private final Sensor mLightSensor;
+ private final Optional<Sensor> mLightSensorOptional;
private final int[] mSensorToBrightness;
private final int[] mSensorToScrimOpacity;
@@ -73,16 +73,16 @@
* --ei brightness_bucket 1}
*/
private int mDebugBrightnessBucket = -1;
- private DozeMachine.State mState;
@Inject
public DozeScreenBrightness(Context context, @WrappedService DozeMachine.Service service,
- AsyncSensorManager sensorManager, @Nullable @BrightnessSensor Sensor lightSensor,
- DozeHost host, Handler handler, AlwaysOnDisplayPolicy alwaysOnDisplayPolicy) {
+ AsyncSensorManager sensorManager,
+ @BrightnessSensor Optional<Sensor> lightSensorOptional, DozeHost host, Handler handler,
+ AlwaysOnDisplayPolicy alwaysOnDisplayPolicy) {
mContext = context;
mDozeService = service;
mSensorManager = sensorManager;
- mLightSensor = lightSensor;
+ mLightSensorOptional = lightSensorOptional;
mDozeHost = host;
mHandler = handler;
@@ -93,7 +93,6 @@
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
- mState = newState;
switch (newState) {
case INITIALIZED:
case DOZE:
@@ -111,10 +110,7 @@
@Override
public void onScreenState(int state) {
- if (!mScreenOff
- && (mState == DozeMachine.State.DOZE_AOD
- || mState == DozeMachine.State.DOZE_AOD_DOCKED)
- && (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND)) {
+ if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND) {
setLightSensorEnabled(true);
} else {
setLightSensorEnabled(false);
@@ -149,7 +145,7 @@
}
int scrimOpacity = -1;
- if (mLightSensor == null) {
+ if (!mLightSensorOptional.isPresent()) {
// No light sensor, scrims are always transparent.
scrimOpacity = 0;
} else if (brightnessReady) {
@@ -193,9 +189,9 @@
}
private void setLightSensorEnabled(boolean enabled) {
- if (enabled && !mRegistered && mLightSensor != null) {
+ if (enabled && !mRegistered && mLightSensorOptional.isPresent()) {
// Wait until we get an event from the sensor until indicating ready.
- mRegistered = mSensorManager.registerListener(this, mLightSensor,
+ mRegistered = mSensorManager.registerListener(this, mLightSensorOptional.get(),
SensorManager.SENSOR_DELAY_NORMAL, mHandler);
mLastSensorValue = -1;
} else if (!enabled && mRegistered) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 524d9c8..028870f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -150,6 +150,15 @@
true /* reports touch coordinates */,
true /* touchscreen */,
dozeLog),
+ new TriggerSensor(
+ findSensorWithType(config.udfpsLongPressSensorType()),
+ Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
+ false /* settingDef */,
+ true /* configured */,
+ DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS,
+ true /* reports touch coordinates */,
+ true /* touchscreen */,
+ dozeLog),
new PluginSensor(
new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 8364b48..45e5c61 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -39,6 +39,7 @@
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
+import com.android.systemui.biometrics.AuthController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.dagger.DozeScope;
@@ -93,6 +94,7 @@
private final DockManager mDockManager;
private final ProximitySensor.ProximityCheck mProxCheck;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final AuthController mAuthController;
private long mNotificationPulseTime;
private boolean mPulsePending;
@@ -165,7 +167,7 @@
WakeLock wakeLock, DockManager dockManager,
ProximitySensor proximitySensor, ProximitySensor.ProximityCheck proxCheck,
DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher,
- SecureSettings secureSettings) {
+ SecureSettings secureSettings, AuthController authController) {
mContext = context;
mDozeHost = dozeHost;
mConfig = config;
@@ -181,6 +183,7 @@
mProxCheck = proxCheck;
mDozeLog = dozeLog;
mBroadcastDispatcher = broadcastDispatcher;
+ mAuthController = authController;
}
@Override
@@ -256,6 +259,7 @@
boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
+ boolean isUdfpsLongPress = pulseReason == DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS;
boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0;
if (isWakeDisplay) {
@@ -281,6 +285,11 @@
gentleWakeUp(pulseReason);
} else if (isPickup) {
gentleWakeUp(pulseReason);
+ } else if (isUdfpsLongPress) {
+ gentleWakeUp(pulseReason);
+ // Since the gesture won't be received by the UDFPS view, manually inject an
+ // event.
+ mAuthController.onAodInterrupt((int) screenX, (int) screenY);
} else {
mDozeHost.extendPulse(pulseReason);
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
index b899460..9c6e02a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
@@ -20,8 +20,6 @@
import android.hardware.Sensor;
import android.os.Handler;
-import androidx.annotation.Nullable;
-
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeAuthRemover;
@@ -44,6 +42,8 @@
import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
+import java.util.Optional;
+
import dagger.Module;
import dagger.Provides;
@@ -93,9 +93,9 @@
@Provides
@BrightnessSensor
- @Nullable
- static Sensor providesBrightnessSensor(AsyncSensorManager sensorManager, Context context) {
- return DozeSensors.findSensorWithType(sensorManager,
- context.getString(R.string.doze_brightness_sensor_type));
+ static Optional<Sensor> providesBrightnessSensor(
+ AsyncSensorManager sensorManager, Context context) {
+ return Optional.ofNullable(DozeSensors.findSensorWithType(sensorManager,
+ context.getString(R.string.doze_brightness_sensor_type)));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
index f01713f..f695622 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
@@ -52,9 +52,12 @@
private val packageControllers: LinkedHashMap<String, MutableList<MediaController>> =
LinkedHashMap()
- // Keep track of the key used for the session tokens. This information is used to know when
+ // Keep track of the key used for the session tokens. This information is used to know when to
// dispatch a removed event so that a media object for a local session will be removed.
- private val keyedTokens: MutableMap<String, MutableList<MediaSession.Token>> = mutableMapOf()
+ private val keyedTokens: MutableMap<String, MutableSet<MediaSession.Token>> = mutableMapOf()
+
+ // Keep track of which media session tokens have associated notifications.
+ private val tokensWithNotifications: MutableSet<MediaSession.Token> = mutableSetOf()
private val sessionListener = object : MediaSessionManager.OnActiveSessionsChangedListener {
override fun onActiveSessionsChanged(controllers: List<MediaController>) {
@@ -90,6 +93,9 @@
*/
override fun onMediaDataLoaded(key: String, oldKey: String?, info: MediaData) {
backgroundExecutor.execute {
+ info.token?.let {
+ tokensWithNotifications.add(it)
+ }
val isMigration = oldKey != null && key != oldKey
if (isMigration) {
keyedTokens.remove(oldKey)?.let { removed -> keyedTokens.put(key, removed) }
@@ -99,7 +105,7 @@
tokens ->
tokens.add(info.token)
} ?: run {
- val tokens = mutableListOf(info.token)
+ val tokens = mutableSetOf(info.token)
keyedTokens.put(key, tokens)
}
}
@@ -110,7 +116,8 @@
}
// Limiting search to only apps with a single remote session.
val remote = if (remoteControllers?.size == 1) remoteControllers.firstOrNull() else null
- if (isMigration || remote == null || remote.sessionToken == info.token) {
+ if (isMigration || remote == null || remote.sessionToken == info.token ||
+ !tokensWithNotifications.contains(remote.sessionToken)) {
// Not filtering in this case. Passing the event along to listeners.
dispatchMediaDataLoaded(key, oldKey, info)
} else {
@@ -159,5 +166,6 @@
packageControllers.put(controller.packageName, tokens)
}
}
+ tokensWithNotifications.retainAll(controllers.map { it.sessionToken })
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
new file mode 100644
index 0000000..9fc64d5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -0,0 +1,163 @@
+/*
+ * 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 android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
+
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
+import android.text.SpannableString;
+import android.text.style.ForegroundColorSpan;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+
+import com.android.settingslib.Utils;
+import com.android.settingslib.media.LocalMediaManager.MediaDeviceState;
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.R;
+
+import java.util.List;
+
+/**
+ * Adapter for media output dialog.
+ */
+public class MediaOutputAdapter extends MediaOutputBaseAdapter {
+
+ private static final String TAG = "MediaOutputAdapter";
+ private static final int PAIR_NEW = 1;
+
+ public MediaOutputAdapter(MediaOutputController controller) {
+ super(controller);
+ }
+
+ @Override
+ public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
+ int viewType) {
+ super.onCreateViewHolder(viewGroup, viewType);
+
+ return new MediaDeviceViewHolder(mHolderView);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull MediaDeviceBaseViewHolder viewHolder, int position) {
+ if (mController.isZeroMode() && position == (mController.getMediaDevices().size())) {
+ viewHolder.onBind(PAIR_NEW);
+ } else if (position < (mController.getMediaDevices().size())) {
+ viewHolder.onBind(((List<MediaDevice>) (mController.getMediaDevices())).get(position));
+ } else {
+ Log.d(TAG, "Incorrect position: " + position);
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ if (mController.isZeroMode()) {
+ // Add extra one for "pair new"
+ return mController.getMediaDevices().size() + 1;
+ }
+ return mController.getMediaDevices().size();
+ }
+
+ void onItemClick(MediaDevice device) {
+ mController.connectDevice(device);
+ device.setState(MediaDeviceState.STATE_CONNECTING);
+ notifyDataSetChanged();
+ }
+
+ void onItemClick(int customizedItem) {
+ if (customizedItem == PAIR_NEW) {
+ mController.launchBluetoothPairing();
+ }
+ }
+
+ @Override
+ CharSequence getItemTitle(MediaDevice device) {
+ if (device.getDeviceType() == MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE
+ && !device.isConnected()) {
+ final CharSequence deviceName = device.getName();
+ // Append status to title only for the disconnected Bluetooth device.
+ final SpannableString spannableTitle = new SpannableString(
+ mContext.getString(R.string.media_output_dialog_disconnected, deviceName));
+ spannableTitle.setSpan(new ForegroundColorSpan(
+ Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorSecondary)),
+ deviceName.length(),
+ spannableTitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE);
+ return spannableTitle;
+ }
+ return super.getItemTitle(device);
+ }
+
+ class MediaDeviceViewHolder extends MediaDeviceBaseViewHolder {
+
+ MediaDeviceViewHolder(View view) {
+ super(view);
+ }
+
+ @Override
+ void onBind(MediaDevice device) {
+ super.onBind(device);
+ if (mController.isTransferring()) {
+ if (device.getState() == MediaDeviceState.STATE_CONNECTING
+ && !mController.hasAdjustVolumeUserRestriction()) {
+ setTwoLineLayout(device, true);
+ mProgressBar.setVisibility(View.VISIBLE);
+ mSeekBar.setVisibility(View.GONE);
+ mSubTitleText.setVisibility(View.GONE);
+ } else {
+ setSingleLineLayout(getItemTitle(device), false);
+ }
+ } else {
+ // Set different layout for each device
+ if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) {
+ setTwoLineLayout(device, false);
+ mSubTitleText.setVisibility(View.VISIBLE);
+ mSeekBar.setVisibility(View.GONE);
+ mProgressBar.setVisibility(View.GONE);
+ mSubTitleText.setText(R.string.media_output_dialog_connect_failed);
+ mFrameLayout.setOnClickListener(v -> onItemClick(device));
+ } else if (!mController.hasAdjustVolumeUserRestriction()
+ && isCurrentConnected(device)) {
+ setTwoLineLayout(device, true);
+ mSeekBar.setVisibility(View.VISIBLE);
+ mProgressBar.setVisibility(View.GONE);
+ mSubTitleText.setVisibility(View.GONE);
+ initSeekbar(device);
+ } else {
+ setSingleLineLayout(getItemTitle(device), false);
+ mFrameLayout.setOnClickListener(v -> onItemClick(device));
+ }
+ }
+ }
+
+ @Override
+ void onBind(int customizedItem) {
+ if (customizedItem == PAIR_NEW) {
+ setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new),
+ false);
+ 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(PAIR_NEW));
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
new file mode 100644
index 0000000..7579c25
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.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.systemui.media.dialog;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.RelativeLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.R;
+
+/**
+ * Base adapter for media output dialog.
+ */
+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";
+
+ final MediaOutputController mController;
+
+ private boolean mIsDragging;
+
+ Context mContext;
+ View mHolderView;
+
+ public MediaOutputBaseAdapter(MediaOutputController controller) {
+ mController = controller;
+ mIsDragging = false;
+ }
+
+ @Override
+ public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
+ int viewType) {
+ mContext = viewGroup.getContext();
+ mHolderView = LayoutInflater.from(mContext).inflate(R.layout.media_output_list_item,
+ viewGroup, false);
+
+ return null;
+ }
+
+ CharSequence getItemTitle(MediaDevice device) {
+ return device.getName();
+ }
+
+ boolean isCurrentConnected(MediaDevice device) {
+ return TextUtils.equals(device.getId(),
+ mController.getCurrentConnectedMediaDevice().getId());
+ }
+
+ boolean isDragging() {
+ return mIsDragging;
+ }
+
+ /**
+ * ViewHolder for binding device view.
+ */
+ abstract class MediaDeviceBaseViewHolder extends RecyclerView.ViewHolder {
+ final FrameLayout mFrameLayout;
+ final TextView mTitleText;
+ final TextView mTwoLineTitleText;
+ final TextView mSubTitleText;
+ final ImageView mTitleIcon;
+ final ImageView mEndIcon;
+ final ProgressBar mProgressBar;
+ final SeekBar mSeekBar;
+ final RelativeLayout mTwoLineLayout;
+
+ MediaDeviceBaseViewHolder(View view) {
+ super(view);
+ mFrameLayout = 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);
+ }
+
+ void onBind(MediaDevice device) {
+ mTitleIcon.setImageIcon(mController.getDeviceIconCompat(device).toIcon(mContext));
+ }
+
+ void onBind(int customizedItem) { }
+
+ void setSingleLineLayout(CharSequence title, boolean bFocused) {
+ mTitleText.setVisibility(View.VISIBLE);
+ mTwoLineLayout.setVisibility(View.GONE);
+ mTitleText.setText(title);
+ if (bFocused) {
+ mTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, Typeface.NORMAL));
+ } else {
+ mTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL));
+ }
+ }
+
+ void setTwoLineLayout(MediaDevice device, boolean bFocused) {
+ mTitleText.setVisibility(View.GONE);
+ mTwoLineLayout.setVisibility(View.VISIBLE);
+ mTwoLineTitleText.setText(getItemTitle(device));
+ if (bFocused) {
+ mTwoLineTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE,
+ Typeface.NORMAL));
+ } else {
+ mTwoLineTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL));
+ }
+ }
+
+ void initSeekbar(MediaDevice device) {
+ mSeekBar.setMax(device.getMaxVolume());
+ mSeekBar.setMin(0);
+ if (mSeekBar.getProgress() != device.getCurrentVolume()) {
+ mSeekBar.setProgress(device.getCurrentVolume());
+ }
+ mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (device == null || !fromUser) {
+ return;
+ }
+ mController.adjustVolume(device, progress);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ mIsDragging = true;
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ mIsDragging = false;
+ }
+ });
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
new file mode 100644
index 0000000..f8f4f4d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.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 static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.Window;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.core.graphics.drawable.IconCompat;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+
+/**
+ * Base dialog for media output UI
+ */
+public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
+ MediaOutputController.Callback {
+
+ private static final String TAG = "MediaOutputDialog";
+
+ private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
+ private final RecyclerView.LayoutManager mLayoutManager;
+
+ final Context mContext;
+ final MediaOutputController mMediaOutputController;
+
+ @VisibleForTesting
+ View mDialogView;
+ private TextView mHeaderTitle;
+ private TextView mHeaderSubtitle;
+ private ImageView mHeaderIcon;
+ private RecyclerView mDevicesRecyclerView;
+ private LinearLayout mDeviceListLayout;
+ private Button mDoneButton;
+ private Button mStopButton;
+ private View mListBottomPadding;
+ private int mListMaxHeight;
+
+ MediaOutputBaseAdapter mAdapter;
+ FrameLayout mGroupItemController;
+ View mGroupDivider;
+
+ private final ViewTreeObserver.OnGlobalLayoutListener mDeviceListLayoutListener = () -> {
+ // Set max height for list
+ if (mDeviceListLayout.getHeight() > mListMaxHeight) {
+ ViewGroup.LayoutParams params = mDeviceListLayout.getLayoutParams();
+ params.height = mListMaxHeight;
+ mDeviceListLayout.setLayoutParams(params);
+ }
+ };
+
+ public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController) {
+ super(context, R.style.Theme_SystemUI_Dialog_MediaOutput);
+ mContext = context;
+ mMediaOutputController = mediaOutputController;
+ mLayoutManager = new LinearLayoutManager(mContext);
+ mListMaxHeight = context.getResources().getDimensionPixelSize(
+ R.dimen.media_output_dialog_list_max_height);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mDialogView = LayoutInflater.from(mContext).inflate(R.layout.media_output_dialog, null);
+ final Window window = getWindow();
+ final WindowManager.LayoutParams lp = window.getAttributes();
+ lp.gravity = Gravity.BOTTOM;
+ // Config insets to make sure the layout is above the navigation bar
+ lp.setFitInsetsTypes(statusBars() | navigationBars());
+ lp.setFitInsetsSides(WindowInsets.Side.all());
+ lp.setFitInsetsIgnoringVisibility(true);
+ window.setAttributes(lp);
+ window.setContentView(mDialogView);
+ window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+ mHeaderTitle = mDialogView.requireViewById(R.id.header_title);
+ mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle);
+ mHeaderIcon = mDialogView.requireViewById(R.id.header_icon);
+ mDevicesRecyclerView = mDialogView.requireViewById(R.id.list_result);
+ mGroupItemController = mDialogView.requireViewById(R.id.group_item_controller);
+ mGroupDivider = mDialogView.requireViewById(R.id.group_item_divider);
+ mDeviceListLayout = mDialogView.requireViewById(R.id.device_list);
+ mDoneButton = mDialogView.requireViewById(R.id.done);
+ mStopButton = mDialogView.requireViewById(R.id.stop);
+ mListBottomPadding = mDialogView.requireViewById(R.id.list_bottom_padding);
+
+ mDeviceListLayout.getViewTreeObserver().addOnGlobalLayoutListener(
+ mDeviceListLayoutListener);
+ // Init device list
+ mDevicesRecyclerView.setLayoutManager(mLayoutManager);
+ mDevicesRecyclerView.setAdapter(mAdapter);
+ // Init bottom buttons
+ mDoneButton.setOnClickListener(v -> dismiss());
+ mStopButton.setOnClickListener(v -> {
+ mMediaOutputController.releaseSession();
+ dismiss();
+ });
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ mMediaOutputController.start(this);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ mMediaOutputController.stop();
+ }
+
+ @VisibleForTesting
+ void refresh() {
+ // Update header icon
+ final int iconRes = getHeaderIconRes();
+ final IconCompat iconCompat = getHeaderIcon();
+ if (iconRes != 0) {
+ mHeaderIcon.setVisibility(View.VISIBLE);
+ mHeaderIcon.setImageResource(iconRes);
+ } else if (iconCompat != null) {
+ mHeaderIcon.setVisibility(View.VISIBLE);
+ mHeaderIcon.setImageIcon(iconCompat.toIcon(mContext));
+ } else {
+ mHeaderIcon.setVisibility(View.GONE);
+ }
+ if (mHeaderIcon.getVisibility() == View.VISIBLE) {
+ final int size = getHeaderIconSize();
+ mHeaderIcon.setLayoutParams(new LinearLayout.LayoutParams(size, size));
+ }
+ // Update title and subtitle
+ mHeaderTitle.setText(getHeaderText());
+ final CharSequence subTitle = getHeaderSubtitle();
+ if (TextUtils.isEmpty(subTitle)) {
+ mHeaderSubtitle.setVisibility(View.GONE);
+ mHeaderTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
+ } else {
+ mHeaderSubtitle.setVisibility(View.VISIBLE);
+ mHeaderSubtitle.setText(subTitle);
+ mHeaderTitle.setGravity(Gravity.NO_GRAVITY);
+ }
+ if (!mAdapter.isDragging()) {
+ mAdapter.notifyDataSetChanged();
+ }
+ // Add extra padding when device amount is less than 6
+ if (mMediaOutputController.getMediaDevices().size() < 6) {
+ mListBottomPadding.setVisibility(View.VISIBLE);
+ } else {
+ mListBottomPadding.setVisibility(View.GONE);
+ }
+ }
+
+ abstract int getHeaderIconRes();
+
+ abstract IconCompat getHeaderIcon();
+
+ abstract int getHeaderIconSize();
+
+ abstract CharSequence getHeaderText();
+
+ abstract CharSequence getHeaderSubtitle();
+
+ @Override
+ public void onMediaChanged() {
+ mMainThreadHandler.post(() -> refresh());
+ }
+
+ @Override
+ public void onMediaStoppedOrPaused() {
+ if (isShowing()) {
+ dismiss();
+ }
+ }
+
+ @Override
+ public void onRouteChanged() {
+ mMainThreadHandler.post(() -> refresh());
+ }
+
+ @Override
+ public void dismissDialog() {
+ dismiss();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
new file mode 100644
index 0000000..64d20a27
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -0,0 +1,445 @@
+/*
+ * 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.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.RoutingSessionInfo;
+import android.media.session.MediaController;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.core.graphics.drawable.IconCompat;
+
+import com.android.settingslib.RestrictedLockUtilsInternal;
+import com.android.settingslib.Utils;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.media.InfoMediaManager;
+import com.android.settingslib.media.LocalMediaManager;
+import com.android.settingslib.media.MediaDevice;
+import com.android.settingslib.media.MediaOutputSliceConstants;
+import com.android.settingslib.utils.ThreadUtils;
+import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.phone.ShadeController;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.inject.Inject;
+
+/**
+ * Controller for media output dialog
+ */
+public class MediaOutputController implements LocalMediaManager.DeviceCallback{
+
+ private static final String TAG = "MediaOutputController";
+ private static final boolean DEBUG = false;
+
+ private final String mPackageName;
+ private final Context mContext;
+ private final MediaSessionManager mMediaSessionManager;
+ private final ShadeController mShadeController;
+ private final ActivityStarter mActivityStarter;
+ @VisibleForTesting
+ final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
+
+ private MediaController mMediaController;
+ @VisibleForTesting
+ Callback mCallback;
+ @VisibleForTesting
+ LocalMediaManager mLocalMediaManager;
+
+ @Inject
+ public MediaOutputController(@NonNull Context context, String packageName,
+ MediaSessionManager mediaSessionManager, LocalBluetoothManager
+ lbm, ShadeController shadeController, ActivityStarter starter) {
+ mContext = context;
+ mPackageName = packageName;
+ mMediaSessionManager = mediaSessionManager;
+ mShadeController = shadeController;
+ mActivityStarter = starter;
+ InfoMediaManager imm = new InfoMediaManager(mContext, packageName, null, lbm);
+ mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
+ }
+
+ void start(@NonNull Callback cb) {
+ mMediaDevices.clear();
+ if (!TextUtils.isEmpty(mPackageName)) {
+ for (MediaController controller : mMediaSessionManager.getActiveSessions(null)) {
+ if (TextUtils.equals(controller.getPackageName(), mPackageName)) {
+ mMediaController = controller;
+ mMediaController.unregisterCallback(mCb);
+ mMediaController.registerCallback(mCb);
+ break;
+ }
+ }
+ }
+ if (mMediaController == null) {
+ if (DEBUG) {
+ Log.d(TAG, "No media controller for " + mPackageName);
+ }
+ }
+ if (mLocalMediaManager == null) {
+ if (DEBUG) {
+ Log.d(TAG, "No local media manager " + mPackageName);
+ }
+ return;
+ }
+ mCallback = cb;
+ mLocalMediaManager.unregisterCallback(this);
+ mLocalMediaManager.stopScan();
+ mLocalMediaManager.registerCallback(this);
+ mLocalMediaManager.startScan();
+ }
+
+ void stop() {
+ if (mMediaController != null) {
+ mMediaController.unregisterCallback(mCb);
+ }
+ if (mLocalMediaManager != null) {
+ mLocalMediaManager.unregisterCallback(this);
+ mLocalMediaManager.stopScan();
+ }
+ mMediaDevices.clear();
+ }
+
+ @Override
+ public void onDeviceListUpdate(List<MediaDevice> devices) {
+ buildMediaDevices(devices);
+ mCallback.onRouteChanged();
+ }
+
+ @Override
+ public void onSelectedDeviceStateChanged(MediaDevice device,
+ @LocalMediaManager.MediaDeviceState int state) {
+ mCallback.onRouteChanged();
+ }
+
+ @Override
+ public void onDeviceAttributesChanged() {
+ mCallback.onRouteChanged();
+ }
+
+ @Override
+ public void onRequestFailed(int reason) {
+ mCallback.onRouteChanged();
+ }
+
+ CharSequence getHeaderTitle() {
+ if (mMediaController != null) {
+ final MediaMetadata metadata = mMediaController.getMetadata();
+ if (metadata != null) {
+ return metadata.getDescription().getTitle();
+ }
+ }
+ return mContext.getText(R.string.controls_media_title);
+ }
+
+ CharSequence getHeaderSubTitle() {
+ if (mMediaController == null) {
+ return null;
+ }
+ final MediaMetadata metadata = mMediaController.getMetadata();
+ if (metadata == null) {
+ return null;
+ }
+ return metadata.getDescription().getSubtitle();
+ }
+
+ IconCompat getHeaderIcon() {
+ if (mMediaController == null) {
+ return null;
+ }
+ final MediaMetadata metadata = mMediaController.getMetadata();
+ if (metadata != null) {
+ final Bitmap bitmap = metadata.getDescription().getIconBitmap();
+ if (bitmap != null) {
+ final Bitmap roundBitmap = Utils.convertCornerRadiusBitmap(mContext, bitmap,
+ (float) mContext.getResources().getDimensionPixelSize(
+ R.dimen.media_output_dialog_icon_corner_radius));
+ return IconCompat.createWithBitmap(roundBitmap);
+ }
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Media meta data does not contain icon information");
+ }
+ return getPackageIcon();
+ }
+
+ IconCompat getDeviceIconCompat(MediaDevice device) {
+ Drawable drawable = device.getIcon();
+ if (drawable == null) {
+ if (DEBUG) {
+ Log.d(TAG, "getDeviceIconCompat() device : " + device.getName()
+ + ", drawable is null");
+ }
+ // Use default Bluetooth device icon to handle getIcon() is null case.
+ drawable = mContext.getDrawable(com.android.internal.R.drawable.ic_bt_headphones_a2dp);
+ }
+ return BluetoothUtils.createIconWithDrawable(drawable);
+ }
+
+ private IconCompat getPackageIcon() {
+ 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.");
+ }
+ }
+ return null;
+ }
+
+ private void buildMediaDevices(List<MediaDevice> devices) {
+ // For the first time building list, to make sure the top device is the connected device.
+ if (mMediaDevices.isEmpty()) {
+ final MediaDevice connectedMediaDevice = getCurrentConnectedMediaDevice();
+ if (connectedMediaDevice == null) {
+ if (DEBUG) {
+ Log.d(TAG, "No connected media device.");
+ }
+ mMediaDevices.addAll(devices);
+ return;
+ }
+ for (MediaDevice device : devices) {
+ if (TextUtils.equals(device.getId(), connectedMediaDevice.getId())) {
+ mMediaDevices.add(0, device);
+ } else {
+ mMediaDevices.add(device);
+ }
+ }
+ return;
+ }
+ // To keep the same list order
+ final Collection<MediaDevice> targetMediaDevices = new ArrayList<>();
+ for (MediaDevice originalDevice : mMediaDevices) {
+ for (MediaDevice newDevice : devices) {
+ if (TextUtils.equals(originalDevice.getId(), newDevice.getId())) {
+ targetMediaDevices.add(newDevice);
+ break;
+ }
+ }
+ }
+ if (targetMediaDevices.size() != devices.size()) {
+ devices.removeAll(targetMediaDevices);
+ targetMediaDevices.addAll(devices);
+ }
+ mMediaDevices.clear();
+ mMediaDevices.addAll(targetMediaDevices);
+ }
+
+ void connectDevice(MediaDevice device) {
+ ThreadUtils.postOnBackgroundThread(() -> {
+ mLocalMediaManager.connectDevice(device);
+ });
+ }
+
+ Collection<MediaDevice> getMediaDevices() {
+ return mMediaDevices;
+ }
+
+ MediaDevice getCurrentConnectedMediaDevice() {
+ return mLocalMediaManager.getCurrentConnectedDevice();
+ }
+
+ private MediaDevice getMediaDeviceById(String id) {
+ return mLocalMediaManager.getMediaDeviceById(new ArrayList<>(mMediaDevices), id);
+ }
+
+ boolean addDeviceToPlayMedia(MediaDevice device) {
+ return mLocalMediaManager.addDeviceToPlayMedia(device);
+ }
+
+ boolean removeDeviceFromPlayMedia(MediaDevice device) {
+ return mLocalMediaManager.removeDeviceFromPlayMedia(device);
+ }
+
+ List<MediaDevice> getSelectableMediaDevice() {
+ return mLocalMediaManager.getSelectableMediaDevice();
+ }
+
+ List<MediaDevice> getSelectedMediaDevice() {
+ return mLocalMediaManager.getSelectedMediaDevice();
+ }
+
+ List<MediaDevice> getDeselectableMediaDevice() {
+ 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);
+ }
+
+ void adjustSessionVolume(int volume) {
+ mLocalMediaManager.adjustSessionVolume(volume);
+ }
+
+ int getSessionVolumeMax() {
+ return mLocalMediaManager.getSessionVolumeMax();
+ }
+
+ int getSessionVolume() {
+ return mLocalMediaManager.getSessionVolume();
+ }
+
+ CharSequence getSessionName() {
+ return mLocalMediaManager.getSessionName();
+ }
+
+ void releaseSession() {
+ mLocalMediaManager.releaseSession();
+ }
+
+ List<RoutingSessionInfo> getActiveRemoteMediaDevices() {
+ final List<RoutingSessionInfo> sessionInfos = new ArrayList<>();
+ for (RoutingSessionInfo info : mLocalMediaManager.getActiveMediaSession()) {
+ if (!info.isSystemSession()) {
+ sessionInfos.add(info);
+ }
+ }
+ return sessionInfos;
+ }
+
+ void adjustVolume(MediaDevice device, int volume) {
+ ThreadUtils.postOnBackgroundThread(() -> {
+ device.requestSetVolume(volume);
+ });
+ }
+
+ String getPackageName() {
+ return mPackageName;
+ }
+
+ boolean hasAdjustVolumeUserRestriction() {
+ if (RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+ mContext, UserManager.DISALLOW_ADJUST_VOLUME, UserHandle.myUserId()) != null) {
+ return true;
+ }
+ final UserManager um = mContext.getSystemService(UserManager.class);
+ return um.hasBaseUserRestriction(UserManager.DISALLOW_ADJUST_VOLUME,
+ UserHandle.of(UserHandle.myUserId()));
+ }
+
+ boolean isTransferring() {
+ for (MediaDevice device : mMediaDevices) {
+ if (device.getState() == LocalMediaManager.MediaDeviceState.STATE_CONNECTING) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean isZeroMode() {
+ if (mMediaDevices.size() == 1) {
+ final MediaDevice device = mMediaDevices.iterator().next();
+ // Add "pair new" only when local output device exists
+ final int type = device.getDeviceType();
+ if (type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE
+ || type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE
+ || type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void launchBluetoothPairing() {
+ mCallback.dismissDialog();
+ final ActivityStarter.OnDismissAction postKeyguardAction = () -> {
+ mContext.sendBroadcast(new Intent()
+ .setAction(MediaOutputSliceConstants.ACTION_LAUNCH_BLUETOOTH_PAIRING)
+ .setPackage(MediaOutputSliceConstants.SETTINGS_PACKAGE_NAME));
+ mShadeController.animateCollapsePanels();
+ return true;
+ };
+ mActivityStarter.dismissKeyguardThenExecute(postKeyguardAction, null, true);
+ }
+
+ private final MediaController.Callback mCb = new MediaController.Callback() {
+ @Override
+ public void onMetadataChanged(MediaMetadata metadata) {
+ mCallback.onMediaChanged();
+ }
+
+ @Override
+ public void onPlaybackStateChanged(PlaybackState playbackState) {
+ final int state = playbackState.getState();
+ if (state == PlaybackState.STATE_STOPPED || state == PlaybackState.STATE_PAUSED) {
+ mCallback.onMediaStoppedOrPaused();
+ }
+ }
+ };
+
+ interface Callback {
+ /**
+ * Override to handle the media content updating.
+ */
+ void onMediaChanged();
+
+ /**
+ * Override to handle the media state updating.
+ */
+ void onMediaStoppedOrPaused();
+
+ /**
+ * Override to handle the device updating.
+ */
+ void onRouteChanged();
+
+ /**
+ * Override to dismiss dialog.
+ */
+ void dismissDialog();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
new file mode 100644
index 0000000..ac9d8ce
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -0,0 +1,77 @@
+/*
+ * 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;
+import com.android.systemui.dagger.SysUISingleton;
+
+/**
+ * Dialog for media output transferring.
+ */
+@SysUISingleton
+public class MediaOutputDialog extends MediaOutputBaseDialog {
+
+ MediaOutputDialog(Context context, boolean aboveStatusbar, MediaOutputController
+ mediaOutputController) {
+ super(context, mediaOutputController);
+ mAdapter = new MediaOutputAdapter(mMediaOutputController);
+ if (!aboveStatusbar) {
+ getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+ }
+ show();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mGroupItemController.setVisibility(View.GONE);
+ mGroupDivider.setVisibility(View.GONE);
+ }
+
+ @Override
+ int getHeaderIconRes() {
+ return 0;
+ }
+
+ @Override
+ IconCompat getHeaderIcon() {
+ return mMediaOutputController.getHeaderIcon();
+ }
+
+ @Override
+ int getHeaderIconSize() {
+ return mContext.getResources().getDimensionPixelSize(
+ R.dimen.media_output_dialog_header_album_icon_size);
+ }
+
+ @Override
+ CharSequence getHeaderText() {
+ return mMediaOutputController.getHeaderTitle();
+ }
+
+ @Override
+ CharSequence getHeaderSubtitle() {
+ return mMediaOutputController.getHeaderSubTitle();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
new file mode 100644
index 0000000..bc1dca5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -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 com.android.systemui.media.dialog
+
+import android.content.Context
+import android.media.session.MediaSessionManager
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.phone.ShadeController
+import javax.inject.Inject
+
+/**
+ * Factory to create [MediaOutputDialog] objects.
+ */
+class MediaOutputDialogFactory @Inject constructor(
+ private val context: Context,
+ private val mediaSessionManager: MediaSessionManager,
+ private val lbm: LocalBluetoothManager?,
+ private val shadeController: ShadeController,
+ private val starter: ActivityStarter
+) {
+ /** Creates a [MediaOutputDialog] for the given package. */
+ fun create(packageName: String, aboveStatusBar: Boolean) {
+ MediaOutputController(context, packageName, mediaSessionManager, lbm, shadeController,
+ starter).run {
+ MediaOutputDialog(context, aboveStatusBar, this) }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
new file mode 100644
index 0000000..0ce0c02
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
@@ -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.media.dialog
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.text.TextUtils
+import com.android.settingslib.media.MediaOutputSliceConstants
+import javax.inject.Inject
+
+/**
+ * BroadcastReceiver for handling media output intent
+ */
+class MediaOutputDialogReceiver @Inject constructor(
+ private val mediaOutputDialogFactory: MediaOutputDialogFactory
+) : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (TextUtils.equals(MediaOutputSliceConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG,
+ intent.action)) {
+ mediaOutputDialogFactory.create(
+ intent.getStringExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME), false)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 13b9a55..37c79cc 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -124,7 +124,8 @@
int mNavigationIconHints = 0;
private int mNavBarMode;
- private final Region mActiveRegion = new Region();
+ private final Region mTmpRegion = new Region();
+ private final int[] mTmpPosition = new int[2];
private Rect mTmpBounds = new Rect();
private KeyButtonDrawable mBackIcon;
@@ -266,24 +267,14 @@
private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener = info -> {
// When the nav bar is in 2-button or 3-button mode, or when IME is visible in fully
// gestural mode, the entire nav bar should be touchable.
- if (!mEdgeBackGestureHandler.isHandlingGestures() || mImeVisible) {
+ if (!mEdgeBackGestureHandler.isHandlingGestures()) {
info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
return;
}
info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- ButtonDispatcher imeSwitchButton = getImeSwitchButton();
- if (imeSwitchButton.getVisibility() == VISIBLE) {
- // If the IME is not up, but the ime switch button is visible, then make sure that
- // button is touchable
- int[] loc = new int[2];
- View buttonView = imeSwitchButton.getCurrentView();
- buttonView.getLocationInWindow(loc);
- info.touchableRegion.set(loc[0], loc[1], loc[0] + buttonView.getWidth(),
- loc[1] + buttonView.getHeight());
- return;
- }
- info.touchableRegion.setEmpty();
+ info.touchableRegion.set(getButtonLocations(false /* includeFloatingRotationButton */,
+ false /* inScreen */));
};
private final Consumer<Boolean> mRotationButtonListener = (visible) -> {
@@ -956,30 +947,44 @@
* Notifies the overview service of the active touch regions.
*/
public void notifyActiveTouchRegions() {
- mActiveRegion.setEmpty();
- updateButtonLocation(getBackButton());
- updateButtonLocation(getHomeButton());
- updateButtonLocation(getRecentsButton());
- updateButtonLocation(getImeSwitchButton());
- updateButtonLocation(getAccessibilityButton());
- if (mFloatingRotationButton.isVisible()) {
- View floatingRotationView = mFloatingRotationButton.getCurrentView();
- floatingRotationView.getBoundsOnScreen(mTmpBounds);
- mActiveRegion.op(mTmpBounds, Op.UNION);
- } else {
- updateButtonLocation(getRotateSuggestionButton());
- }
- mOverviewProxyService.onActiveNavBarRegionChanges(mActiveRegion);
+ mOverviewProxyService.onActiveNavBarRegionChanges(
+ getButtonLocations(true /* includeFloatingRotationButton */, true /* inScreen */));
}
- private void updateButtonLocation(ButtonDispatcher button) {
+ private Region getButtonLocations(boolean includeFloatingRotationButton,
+ boolean inScreenSpace) {
+ mTmpRegion.setEmpty();
+ updateButtonLocation(getBackButton(), inScreenSpace);
+ updateButtonLocation(getHomeButton(), inScreenSpace);
+ updateButtonLocation(getRecentsButton(), inScreenSpace);
+ updateButtonLocation(getImeSwitchButton(), inScreenSpace);
+ updateButtonLocation(getAccessibilityButton(), inScreenSpace);
+ if (includeFloatingRotationButton && mFloatingRotationButton.isVisible()) {
+ updateButtonLocation(mFloatingRotationButton.getCurrentView(), inScreenSpace);
+ } else {
+ updateButtonLocation(getRotateSuggestionButton(), inScreenSpace);
+ }
+ return mTmpRegion;
+ }
+
+ private void updateButtonLocation(ButtonDispatcher button, boolean inScreenSpace) {
View view = button.getCurrentView();
if (view == null || !button.isVisible()) {
return;
}
+ updateButtonLocation(view, inScreenSpace);
+ }
- view.getBoundsOnScreen(mTmpBounds);
- mActiveRegion.op(mTmpBounds, Op.UNION);
+ private void updateButtonLocation(View view, boolean inScreenSpace) {
+ if (inScreenSpace) {
+ view.getBoundsOnScreen(mTmpBounds);
+ } else {
+ view.getLocationInWindow(mTmpPosition);
+ mTmpBounds.set(mTmpPosition[0], mTmpPosition[1],
+ mTmpPosition[0] + view.getWidth(),
+ mTmpPosition[1] + view.getHeight());
+ }
+ mTmpRegion.op(mTmpBounds, Op.UNION);
}
private void updateOrientationViews() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
index c704d32..65d4e23 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
@@ -17,15 +17,12 @@
package com.android.systemui.navigationbar;
import static android.content.Intent.ACTION_OVERLAY_CHANGED;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.om.IOverlayManager;
-import android.content.om.OverlayInfo;
import android.content.pm.PackageManager;
import android.content.res.ApkAssets;
import android.os.PatternMatcher;
@@ -131,9 +128,6 @@
public void updateCurrentInteractionMode(boolean notify) {
mCurrentUserContext = getCurrentUserContext();
int mode = getCurrentInteractionMode(mCurrentUserContext);
- if (mode == NAV_BAR_MODE_GESTURAL) {
- switchToDefaultGestureNavOverlayIfNecessary();
- }
mUiBgExecutor.execute(() ->
Settings.Secure.putString(mCurrentUserContext.getContentResolver(),
Secure.NAVIGATION_MODE, String.valueOf(mode)));
@@ -187,35 +181,6 @@
}
}
- private void switchToDefaultGestureNavOverlayIfNecessary() {
- final int userId = mCurrentUserContext.getUserId();
- try {
- final IOverlayManager om = IOverlayManager.Stub.asInterface(
- ServiceManager.getService(Context.OVERLAY_SERVICE));
- final OverlayInfo info = om.getOverlayInfo(NAV_BAR_MODE_GESTURAL_OVERLAY, userId);
- if (info != null && !info.isEnabled()) {
- // Enable the default gesture nav overlay, and move the back gesture inset scale to
- // Settings.Secure for left and right sensitivity.
- final int curInset = mCurrentUserContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.config_backGestureInset);
- om.setEnabledExclusiveInCategory(NAV_BAR_MODE_GESTURAL_OVERLAY, userId);
- final int defInset = mCurrentUserContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.config_backGestureInset);
-
- final float scale = defInset == 0 ? 1.0f : ((float) curInset) / defInset;
- Settings.Secure.putFloat(mCurrentUserContext.getContentResolver(),
- Secure.BACK_GESTURE_INSET_SCALE_LEFT, scale);
- Settings.Secure.putFloat(mCurrentUserContext.getContentResolver(),
- Secure.BACK_GESTURE_INSET_SCALE_RIGHT, scale);
- if (DEBUG) {
- Log.v(TAG, "Moved back sensitivity for user " + userId + " to scale " + scale);
- }
- }
- } catch (SecurityException | IllegalStateException | RemoteException e) {
- Log.e(TAG, "Failed to switch to default gesture nav overlay for user " + userId);
- }
- }
-
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("NavigationModeController:");
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackGestureTfClassifierProvider.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackGestureTfClassifierProvider.java
new file mode 100644
index 0000000..fbed183
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackGestureTfClassifierProvider.java
@@ -0,0 +1,66 @@
+/*
+ * 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.navigationbar.gestural;
+
+import android.content.res.AssetManager;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class can be overridden by a vendor-specific sys UI implementation,
+ * in order to provide classification models for the Back Gesture.
+ */
+public class BackGestureTfClassifierProvider {
+ private static final String TAG = "BackGestureTfClassifierProvider";
+
+ /**
+ * Default implementation that returns an empty map.
+ * This method is overridden in vendor-specific Sys UI implementation.
+ *
+ * @param am An AssetManager to get the vocab file.
+ */
+ public Map<String, Integer> loadVocab(AssetManager am) {
+ return new HashMap<String, Integer>();
+ }
+
+ /**
+ * This method is overridden in vendor-specific Sys UI implementation.
+ *
+ * @param featuresVector List of input features.
+ *
+ */
+ public float predict(Object[] featuresVector) {
+ return -1;
+ }
+
+ /**
+ * Interpreter owns resources. This method releases the resources after
+ * use to avoid memory leak.
+ * This method is overridden in vendor-specific Sys UI implementation.
+ *
+ */
+ public void release() {}
+
+ /**
+ * Returns whether to use the ML model for Back Gesture.
+ * This method is overridden in vendor-specific Sys UI implementation.
+ *
+ */
+ public boolean isActive() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index dfc82f1..5694360 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -40,7 +40,6 @@
import android.util.TypedValue;
import android.view.Choreographer;
import android.view.ISystemGestureExclusionListener;
-import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputMonitor;
@@ -56,6 +55,7 @@
import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.model.SysUiState;
@@ -79,6 +79,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.Executor;
/**
@@ -120,8 +121,31 @@
public void onTaskStackChanged() {
mGestureBlockingActivityRunning = isGestureBlockingActivityRunning();
}
+ @Override
+ public void onTaskCreated(int taskId, ComponentName componentName) {
+ if (componentName != null) {
+ mPackageName = componentName.getPackageName();
+ } else {
+ mPackageName = "_UNKNOWN";
+ }
+ }
};
+ private DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
+ new DeviceConfig.OnPropertiesChangedListener() {
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ if (DeviceConfig.NAMESPACE_SYSTEMUI.equals(properties.getNamespace())
+ && (properties.getKeyset().contains(
+ SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD)
+ || properties.getKeyset().contains(
+ SystemUiDeviceConfigFlags.USE_BACK_GESTURE_ML_MODEL))) {
+ updateMLModelState();
+ }
+ }
+ };
+
+
private final Context mContext;
private final OverviewProxyService mOverviewProxyService;
private final SysUiState mSysUiState;
@@ -177,6 +201,13 @@
private int mRightInset;
private int mSysUiFlags;
+ // For Tf-Lite model.
+ private BackGestureTfClassifierProvider mBackGestureTfClassifierProvider;
+ private Map<String, Integer> mVocab;
+ private boolean mUseMLModel;
+ private float mMLModelThreshold;
+ private String mPackageName;
+
private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
private final NavigationEdgeBackPlugin.BackCallback mBackCallback =
@@ -242,7 +273,6 @@
Log.e(TAG, "Failed to add gesture blocking activities", e);
}
}
-
mLongPressTimeout = Math.min(MAX_LONG_PRESS_TIMEOUT,
ViewConfiguration.getLongPressTimeout());
@@ -357,6 +387,7 @@
mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
mPluginManager.removePluginListener(this);
ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
+ DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
try {
WindowManagerGlobal.getWindowManagerService()
@@ -372,6 +403,9 @@
mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
mContext.getMainThreadHandler());
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+ runnable -> (mContext.getMainThreadHandler()).post(runnable),
+ mOnPropertiesChangedListener);
try {
WindowManagerGlobal.getWindowManagerService()
@@ -393,6 +427,8 @@
mPluginManager.addPluginListener(
this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
}
+ // Update the ML model resources.
+ updateMLModelState();
}
@Override
@@ -445,27 +481,88 @@
}
}
+ private void updateMLModelState() {
+ boolean newState = mIsEnabled && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.USE_BACK_GESTURE_ML_MODEL, false);
+
+ if (newState == mUseMLModel) {
+ return;
+ }
+
+ if (newState) {
+ mBackGestureTfClassifierProvider = SystemUIFactory.getInstance()
+ .createBackGestureTfClassifierProvider(mContext.getAssets());
+ mMLModelThreshold = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD, 0.9f);
+ if (mBackGestureTfClassifierProvider.isActive()) {
+ mVocab = mBackGestureTfClassifierProvider.loadVocab(mContext.getAssets());
+ mUseMLModel = true;
+ return;
+ }
+ }
+
+ mUseMLModel = false;
+ if (mBackGestureTfClassifierProvider != null) {
+ mBackGestureTfClassifierProvider.release();
+ mBackGestureTfClassifierProvider = null;
+ }
+ }
+
+ private float getBackGesturePredictionsCategory(int x, int y) {
+ if (!mVocab.containsKey(mPackageName)) {
+ return -1;
+ }
+
+ int distanceFromEdge;
+ int location;
+ if (x <= mDisplaySize.x / 2.0) {
+ location = 1; // left
+ distanceFromEdge = x;
+ } else {
+ location = 2; // right
+ distanceFromEdge = mDisplaySize.x - x;
+ }
+
+ Object[] featuresVector = {
+ new long[]{(long) mDisplaySize.x},
+ new long[]{(long) distanceFromEdge},
+ new long[]{(long) location},
+ new long[]{(long) mVocab.get(mPackageName)},
+ new long[]{(long) y},
+ };
+
+ final float results = mBackGestureTfClassifierProvider.predict(featuresVector);
+ if (results == -1) return -1;
+
+ return results >= mMLModelThreshold ? 1 : 0;
+ }
+
private boolean isWithinTouchRegion(int x, int y) {
- // Disallow if we are in the bottom gesture area
- if (y >= (mDisplaySize.y - mBottomGestureHeight)) {
- return false;
- }
+ boolean withinRange = false;
+ float results = -1;
- // If the point is way too far (twice the margin), it is
- // not interesting to us for logging purposes, nor we
- // should process it. Simply return false and keep
- // mLogGesture = false.
- if (x > 2 * (mEdgeWidthLeft + mLeftInset)
- && x < (mDisplaySize.x - 2 * (mEdgeWidthRight + mRightInset))) {
- return false;
+ if (mUseMLModel && (results = getBackGesturePredictionsCategory(x, y)) != -1) {
+ withinRange = results == 1 ? true : false;
+ } else {
+ // Disallow if we are in the bottom gesture area
+ if (y >= (mDisplaySize.y - mBottomGestureHeight)) {
+ return false;
+ }
+ // If the point is way too far (twice the margin), it is
+ // not interesting to us for logging purposes, nor we
+ // should process it. Simply return false and keep
+ // mLogGesture = false.
+ if (x > 2 * (mEdgeWidthLeft + mLeftInset)
+ && x < (mDisplaySize.x - 2 * (mEdgeWidthRight + mRightInset))) {
+ return false;
+ }
+ // Denotes whether we should proceed with the gesture.
+ // Even if it is false, we may want to log it assuming
+ // it is not invalid due to exclusion.
+ withinRange = x <= mEdgeWidthLeft + mLeftInset
+ || x >= (mDisplaySize.x - mEdgeWidthRight - mRightInset);
}
- // Denotes whether we should proceed with the gesture.
- // Even if it is false, we may want to log it assuming
- // it is not invalid due to exclusion.
- boolean withinRange = x <= mEdgeWidthLeft + mLeftInset
- || x >= (mDisplaySize.x - mEdgeWidthRight - mRightInset);
-
// Always allow if the user is in a transient sticky immersive state
if (mIsNavBarShownTransiently) {
mLogGesture = true;
@@ -666,6 +763,11 @@
ActivityManager.RunningTaskInfo runningTask =
ActivityManagerWrapper.getInstance().getRunningTask();
ComponentName topActivity = runningTask == null ? null : runningTask.topActivity;
+ if (topActivity != null) {
+ mPackageName = topActivity.getPackageName();
+ } else {
+ mPackageName = "_UNKNOWN";
+ }
return topActivity != null && mGestureBlockingActivities.contains(topActivity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
index fd8ca80..419d8d5 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -109,10 +109,11 @@
@SuppressWarnings("unchecked")
PipTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds, Rect endBounds,
- Rect sourceHintRect) {
+ Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction) {
if (mCurrentAnimator == null) {
mCurrentAnimator = setupPipTransitionAnimator(
- PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect));
+ PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect,
+ direction));
} else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
&& mCurrentAnimator.isRunning()) {
// If we are still animating the fade into pip, then just move the surface and ensure
@@ -127,7 +128,8 @@
} else {
mCurrentAnimator.cancel();
mCurrentAnimator = setupPipTransitionAnimator(
- PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect));
+ PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect,
+ direction));
}
return mCurrentAnimator;
}
@@ -281,7 +283,8 @@
boolean inScaleTransition() {
if (mAnimationType != ANIM_TYPE_BOUNDS) return false;
- return !isInPipDirection(getTransitionDirection());
+ final int direction = getTransitionDirection();
+ return !isInPipDirection(direction) && !isOutPipDirection(direction);
}
/**
@@ -357,16 +360,26 @@
}
static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
- Rect startValue, Rect endValue, Rect sourceHintRect) {
+ Rect startValue, Rect endValue, Rect sourceHintRect,
+ @PipAnimationController.TransitionDirection int direction) {
// Just for simplicity we'll interpolate between the source rect hint insets and empty
// insets to calculate the window crop
- final Rect initialStartValue = new Rect(startValue);
- final Rect sourceHintRectInsets = sourceHintRect != null
- ? new Rect(sourceHintRect.left - startValue.left,
- sourceHintRect.top - startValue.top,
- startValue.right - sourceHintRect.right,
- startValue.bottom - sourceHintRect.bottom)
- : null;
+ final Rect initialSourceValue;
+ if (isOutPipDirection(direction)) {
+ initialSourceValue = new Rect(endValue);
+ } else {
+ initialSourceValue = new Rect(startValue);
+ }
+
+ final Rect sourceHintRectInsets;
+ if (sourceHintRect == null) {
+ sourceHintRectInsets = null;
+ } else {
+ sourceHintRectInsets = new Rect(sourceHintRect.left - initialSourceValue.left,
+ sourceHintRect.top - initialSourceValue.top,
+ initialSourceValue.right - sourceHintRect.right,
+ initialSourceValue.bottom - sourceHintRect.bottom);
+ }
final Rect sourceInsets = new Rect(0, 0, 0, 0);
// construct new Rect instances in case they are recycled
@@ -382,21 +395,23 @@
final Rect end = getEndValue();
Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
setCurrentValue(bounds);
- if (inScaleTransition()) {
- if (isOutPipDirection(getTransitionDirection())) {
+ if (inScaleTransition() || sourceHintRect == null) {
+ if (isOutPipDirection(direction)) {
getSurfaceTransactionHelper().scale(tx, leash, end, bounds);
} else {
getSurfaceTransactionHelper().scale(tx, leash, start, bounds);
}
} else {
- if (sourceHintRectInsets != null) {
- Rect insets = mInsetsEvaluator.evaluate(fraction, sourceInsets,
- sourceHintRectInsets);
- getSurfaceTransactionHelper().scaleAndCrop(tx, leash, initialStartValue,
- bounds, insets);
+ final Rect insets;
+ if (isOutPipDirection(direction)) {
+ insets = mInsetsEvaluator.evaluate(fraction, sourceHintRectInsets,
+ sourceInsets);
} else {
- getSurfaceTransactionHelper().scale(tx, leash, start, bounds);
+ insets = mInsetsEvaluator.evaluate(fraction, sourceInsets,
+ sourceHintRectInsets);
}
+ getSurfaceTransactionHelper().scaleAndCrop(tx, leash,
+ initialSourceValue, bounds, insets);
}
tx.apply();
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index a5ee3a0..b6334c5 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -360,8 +360,8 @@
public void onTransactionReady(int id, SurfaceControl.Transaction t) {
t.apply();
scheduleAnimateResizePip(mLastReportedBounds, destinationBounds,
- null /* sourceHintRect */, direction, animationDurationMs,
- null /* updateBoundsCallback */);
+ getValidSourceHintRect(mTaskInfo, destinationBounds), direction,
+ animationDurationMs, null /* updateBoundsCallback */);
mState = State.EXITING_PIP;
}
});
@@ -551,6 +551,11 @@
return;
}
+ if (mLeash == null) {
+ Log.e(TAG, "PiP Leash is not yet ready.");
+ return;
+ }
+
if (Looper.getMainLooper() != Looper.myLooper()) {
throw new RuntimeException("PipMenuView needs to be attached on the main thread.");
}
@@ -580,6 +585,13 @@
}
}
+ /**
+ * Return whether the PiP Menu has been attached to the leash yet.
+ */
+ public boolean isPipMenuViewHostAttached() {
+ return mPipViewHost != null;
+ }
+
/**
* Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int)}.
@@ -986,7 +998,7 @@
return;
}
mPipAnimationController
- .getAnimator(mLeash, currentBounds, destinationBounds, sourceHintRect)
+ .getAnimator(mLeash, currentBounds, destinationBounds, sourceHintRect, direction)
.setTransitionDirection(direction)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(durationMs)
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java
index 8bcaa8a..22adbb7 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java
@@ -17,6 +17,7 @@
package com.android.systemui.pip;
import android.app.TaskInfo;
+import android.content.pm.PackageManager;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -28,26 +29,48 @@
@SysUISingleton
public class PipUiEventLogger {
+ private static final int INVALID_PACKAGE_UID = -1;
+
private final UiEventLogger mUiEventLogger;
+ private final PackageManager mPackageManager;
- private TaskInfo mTaskInfo;
+ private String mPackageName;
+ private int mPackageUid = INVALID_PACKAGE_UID;
- public PipUiEventLogger(UiEventLogger uiEventLogger) {
+ public PipUiEventLogger(UiEventLogger uiEventLogger, PackageManager packageManager) {
mUiEventLogger = uiEventLogger;
+ mPackageManager = packageManager;
}
public void setTaskInfo(TaskInfo taskInfo) {
- mTaskInfo = taskInfo;
+ if (taskInfo == null) {
+ mPackageName = null;
+ mPackageUid = INVALID_PACKAGE_UID;
+ } else {
+ mPackageName = taskInfo.topActivity.getPackageName();
+ mPackageUid = getUid(mPackageName, taskInfo.userId);
+ }
}
/**
* Sends log via UiEvent, reference go/uievent for how to debug locally
*/
public void log(PipUiEventEnum event) {
- if (mTaskInfo == null) {
+ if (mPackageName == null || mPackageUid == INVALID_PACKAGE_UID) {
return;
}
- mUiEventLogger.log(event, mTaskInfo.userId, mTaskInfo.topActivity.getPackageName());
+ mUiEventLogger.log(event, mPackageUid, mPackageName);
+ }
+
+ private int getUid(String packageName, int userId) {
+ int uid = INVALID_PACKAGE_UID;
+ try {
+ uid = mPackageManager.getApplicationInfoAsUser(
+ packageName, 0 /* ApplicationInfoFlags */, userId).uid;
+ } catch (PackageManager.NameNotFoundException e) {
+ // do nothing.
+ }
+ return uid;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java
index 4f225e2..5bb1794 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java
@@ -280,10 +280,25 @@
PackageManager pm = context.getPackageManager();
boolean supportsPip = pm.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
- if (!supportsPip) {
+ if (supportsPip) {
+ initController(context, broadcastDispatcher, configController, deviceConfig,
+ displayController, floatingContentCoordinator, sysUiState, pipBoundsHandler,
+ pipSurfaceTransactionHelper, pipTaskOrganizer, pipUiEventLogger);
+ } else {
Log.w(TAG, "Device not support PIP feature");
- return;
}
+ }
+
+ private void initController(Context context, BroadcastDispatcher broadcastDispatcher,
+ ConfigurationController configController,
+ DeviceConfigProxy deviceConfig,
+ DisplayController displayController,
+ FloatingContentCoordinator floatingContentCoordinator,
+ SysUiState sysUiState,
+ PipBoundsHandler pipBoundsHandler,
+ PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+ PipTaskOrganizer pipTaskOrganizer,
+ PipUiEventLogger pipUiEventLogger) {
// Ensure that we are the primary user's SystemUI.
final int processUser = UserManager.get(context).getUserHandle();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index d308172..5b07db6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -118,12 +118,7 @@
}
public void onActivityPinned() {
- if (mPipMenuView == null) {
- WindowManager.LayoutParams lp =
- getPipMenuLayoutParams(0, 0);
- mPipMenuView = new PipMenuView(mContext, this);
- mPipTaskOrganizer.attachPipMenuViewHost(mPipMenuView, lp);
- }
+ attachPipMenuView();
mInputConsumerController.registerInputConsumer(true /* withSfVsync */);
}
@@ -140,6 +135,14 @@
}
}
+ private void attachPipMenuView() {
+ if (mPipMenuView == null) {
+ mPipMenuView = new PipMenuView(mContext, this);
+
+ }
+ mPipTaskOrganizer.attachPipMenuViewHost(mPipMenuView, getPipMenuLayoutParams(0, 0));
+ }
+
/**
* Adds a new menu activity listener.
*/
@@ -197,10 +200,11 @@
+ " callers=\n" + Debug.getCallers(5, " "));
}
- if (mPipMenuView == null) {
- Log.e(TAG, "PipMenu has not been attached yet.");
- return;
+ if (!mPipTaskOrganizer.isPipMenuViewHostAttached()) {
+ Log.d(TAG, "PipMenu has not been attached yet. Attaching now at showMenuInternal().");
+ attachPipMenuView();
}
+
mPipMenuView.showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay,
showResizeHandle);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index e738cec..bffeb3e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -14,11 +14,8 @@
package com.android.systemui.qs.customize;
-import android.app.AlertDialog;
-import android.app.AlertDialog.Builder;
import android.content.ComponentName;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
@@ -28,10 +25,11 @@
import android.view.View.OnClickListener;
import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup;
import androidx.recyclerview.widget.ItemTouchHelper;
@@ -49,7 +47,6 @@
import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.tileimpl.QSIconViewImpl;
-import com.android.systemui.statusbar.phone.SystemUIDialog;
import java.util.ArrayList;
import java.util.List;
@@ -78,10 +75,10 @@
private final List<TileInfo> mTiles = new ArrayList<>();
private final ItemTouchHelper mItemTouchHelper;
private final ItemDecoration mDecoration;
- private final AccessibilityManager mAccessibilityManager;
private final int mMinNumTiles;
private int mEditIndex;
private int mTileDividerIndex;
+ private int mFocusIndex;
private boolean mNeedsFocus;
private List<String> mCurrentSpecs;
private List<TileInfo> mOtherTiles;
@@ -90,17 +87,28 @@
private Holder mCurrentDrag;
private int mAccessibilityAction = ACTION_NONE;
private int mAccessibilityFromIndex;
- private CharSequence mAccessibilityFromLabel;
private QSTileHost mHost;
private final UiEventLogger mUiEventLogger;
+ private final AccessibilityDelegateCompat mAccessibilityDelegate;
+ private RecyclerView mRecyclerView;
public TileAdapter(Context context, UiEventLogger uiEventLogger) {
mContext = context;
mUiEventLogger = uiEventLogger;
- mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mItemTouchHelper = new ItemTouchHelper(mCallbacks);
mDecoration = new TileItemDecoration(context);
mMinNumTiles = context.getResources().getInteger(R.integer.quick_settings_min_num_tiles);
+ mAccessibilityDelegate = new TileAdapterDelegate();
+ }
+
+ @Override
+ public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
+ mRecyclerView = recyclerView;
+ }
+
+ @Override
+ public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
+ mRecyclerView = null;
}
public void setHost(QSTileHost host) {
@@ -130,7 +138,6 @@
// Remove blank tile from last spot
mTiles.remove(--mEditIndex);
// Update the tile divider position
- mTileDividerIndex--;
notifyDataSetChanged();
}
mAccessibilityAction = ACTION_NONE;
@@ -241,14 +248,12 @@
}
private void setSelectableForHeaders(View view) {
- if (mAccessibilityManager.isTouchExplorationEnabled()) {
- final boolean selectable = mAccessibilityAction == ACTION_NONE;
- view.setFocusable(selectable);
- view.setImportantForAccessibility(selectable
- ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
- : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
- view.setFocusableInTouchMode(selectable);
- }
+ final boolean selectable = mAccessibilityAction == ACTION_NONE;
+ view.setFocusable(selectable);
+ view.setImportantForAccessibility(selectable
+ ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
+ : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+ view.setFocusableInTouchMode(selectable);
}
@Override
@@ -285,12 +290,11 @@
holder.mTileView.setVisibility(View.VISIBLE);
holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
holder.mTileView.setContentDescription(mContext.getString(
- R.string.accessibility_qs_edit_tile_add, mAccessibilityFromLabel,
- position));
+ R.string.accessibility_qs_edit_tile_add_to_position, position));
holder.mTileView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- selectPosition(holder.getAdapterPosition(), v);
+ selectPosition(holder.getLayoutPosition());
}
});
focusOnHolder(holder);
@@ -299,54 +303,49 @@
TileInfo info = mTiles.get(position);
- if (position > mEditIndex) {
+ final boolean selectable = 0 < position && position < mEditIndex;
+ if (selectable && mAccessibilityAction == ACTION_ADD) {
info.state.contentDescription = mContext.getString(
- R.string.accessibility_qs_edit_add_tile_label, info.state.label);
- } else if (mAccessibilityAction == ACTION_ADD) {
+ R.string.accessibility_qs_edit_tile_add_to_position, position);
+ } else if (selectable && mAccessibilityAction == ACTION_MOVE) {
info.state.contentDescription = mContext.getString(
- R.string.accessibility_qs_edit_tile_add, mAccessibilityFromLabel, position);
- } else if (mAccessibilityAction == ACTION_MOVE) {
- info.state.contentDescription = mContext.getString(
- R.string.accessibility_qs_edit_tile_move, mAccessibilityFromLabel, position);
+ R.string.accessibility_qs_edit_tile_move_to_position, position);
} else {
- info.state.contentDescription = mContext.getString(
- R.string.accessibility_qs_edit_tile_label, position, info.state.label);
+ info.state.contentDescription = info.state.label;
}
+ info.state.expandedAccessibilityClassName = "";
+
holder.mTileView.handleStateChanged(info.state);
holder.mTileView.setShowAppLabel(position > mEditIndex && !info.isSystem);
+ holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ holder.mTileView.setClickable(true);
+ holder.mTileView.setOnClickListener(null);
+ holder.mTileView.setFocusable(true);
+ holder.mTileView.setFocusableInTouchMode(true);
- if (mAccessibilityManager.isTouchExplorationEnabled()) {
- final boolean selectable = mAccessibilityAction == ACTION_NONE || position < mEditIndex;
+ if (mAccessibilityAction != ACTION_NONE) {
holder.mTileView.setClickable(selectable);
holder.mTileView.setFocusable(selectable);
+ holder.mTileView.setFocusableInTouchMode(selectable);
holder.mTileView.setImportantForAccessibility(selectable
? View.IMPORTANT_FOR_ACCESSIBILITY_YES
: View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
- holder.mTileView.setFocusableInTouchMode(selectable);
if (selectable) {
holder.mTileView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- int position = holder.getAdapterPosition();
+ int position = holder.getLayoutPosition();
if (position == RecyclerView.NO_POSITION) return;
if (mAccessibilityAction != ACTION_NONE) {
- selectPosition(position, v);
- } else {
- if (position < mEditIndex && canRemoveTiles()) {
- showAccessibilityDialog(position, v);
- } else if (position < mEditIndex && !canRemoveTiles()) {
- startAccessibleMove(position);
- } else {
- startAccessibleAdd(position);
- }
+ selectPosition(position);
}
}
});
- if (position == mAccessibilityFromIndex) {
- focusOnHolder(holder);
- }
}
}
+ if (position == mFocusIndex) {
+ focusOnHolder(holder);
+ }
}
private void focusOnHolder(Holder holder) {
@@ -360,9 +359,13 @@
int oldLeft, int oldTop, int oldRight, int oldBottom) {
holder.mTileView.removeOnLayoutChangeListener(this);
holder.mTileView.requestFocus();
+ if (mAccessibilityAction == ACTION_NONE) {
+ holder.mTileView.clearFocus();
+ }
}
});
mNeedsFocus = false;
+ mFocusIndex = RecyclerView.NO_POSITION;
}
}
@@ -370,72 +373,77 @@
return mCurrentSpecs.size() > mMinNumTiles;
}
- private void selectPosition(int position, View v) {
+ private void selectPosition(int position) {
if (mAccessibilityAction == ACTION_ADD) {
// Remove the placeholder.
mTiles.remove(mEditIndex--);
- notifyItemRemoved(mEditIndex);
}
mAccessibilityAction = ACTION_NONE;
- move(mAccessibilityFromIndex, position, v);
+ move(mAccessibilityFromIndex, position, false);
+ mFocusIndex = position;
+ mNeedsFocus = true;
notifyDataSetChanged();
}
- private void showAccessibilityDialog(final int position, final View v) {
- final TileInfo info = mTiles.get(position);
- CharSequence[] options = new CharSequence[] {
- mContext.getString(R.string.accessibility_qs_edit_move_tile, info.state.label),
- mContext.getString(R.string.accessibility_qs_edit_remove_tile, info.state.label),
- };
- AlertDialog dialog = new Builder(mContext)
- .setItems(options, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (which == 0) {
- startAccessibleMove(position);
- } else {
- move(position, info.isSystem ? mEditIndex : mTileDividerIndex, v);
- notifyItemChanged(mTileDividerIndex);
- notifyDataSetChanged();
- }
- }
- }).setNegativeButton(android.R.string.cancel, null)
- .create();
- SystemUIDialog.setShowForAllUsers(dialog, true);
- SystemUIDialog.applyFlags(dialog);
- dialog.show();
- }
-
private void startAccessibleAdd(int position) {
mAccessibilityFromIndex = position;
- mAccessibilityFromLabel = mTiles.get(position).state.label;
mAccessibilityAction = ACTION_ADD;
// Add placeholder for last slot.
mTiles.add(mEditIndex++, null);
// Update the tile divider position
mTileDividerIndex++;
+ mFocusIndex = mEditIndex - 1;
mNeedsFocus = true;
+ if (mRecyclerView != null) {
+ mRecyclerView.post(() -> mRecyclerView.smoothScrollToPosition(mFocusIndex));
+ }
notifyDataSetChanged();
}
private void startAccessibleMove(int position) {
mAccessibilityFromIndex = position;
- mAccessibilityFromLabel = mTiles.get(position).state.label;
mAccessibilityAction = ACTION_MOVE;
+ mFocusIndex = position;
mNeedsFocus = true;
notifyDataSetChanged();
}
+ private boolean canRemoveFromPosition(int position) {
+ return canRemoveTiles() && isCurrentTile(position);
+ }
+
+ private boolean isCurrentTile(int position) {
+ return position < mEditIndex;
+ }
+
+ private boolean canAddFromPosition(int position) {
+ return position > mEditIndex;
+ }
+
+ private void addFromPosition(int position) {
+ if (!canAddFromPosition(position)) return;
+ move(position, mEditIndex);
+ }
+
+ private void removeFromPosition(int position) {
+ if (!canRemoveFromPosition(position)) return;
+ TileInfo info = mTiles.get(position);
+ move(position, info.isSystem ? mEditIndex : mTileDividerIndex);
+ }
+
public SpanSizeLookup getSizeLookup() {
return mSizeLookup;
}
- private boolean move(int from, int to, View v) {
+ private boolean move(int from, int to) {
+ return move(from, to, true);
+ }
+
+ private boolean move(int from, int to, boolean notify) {
if (to == from) {
return true;
}
- CharSequence fromLabel = mTiles.get(from).state.label;
- move(from, to, mTiles);
+ move(from, to, mTiles, notify);
updateDividerLocations();
if (to >= mEditIndex) {
mUiEventLogger.log(QSEditEvent.QS_EDIT_REMOVE, 0, strip(mTiles.get(to)));
@@ -477,9 +485,11 @@
return spec;
}
- private <T> void move(int from, int to, List<T> list) {
+ private <T> void move(int from, int to, List<T> list, boolean notify) {
list.add(to, list.remove(from));
- notifyItemMoved(from, to);
+ if (notify) {
+ notifyItemMoved(from, to);
+ }
}
public class Holder extends ViewHolder {
@@ -491,6 +501,8 @@
mTileView = (CustomizeTileView) ((FrameLayout) itemView).getChildAt(0);
mTileView.setBackground(null);
mTileView.getIcon().disableAnimation();
+ mTileView.setTag(this);
+ ViewCompat.setAccessibilityDelegate(mTileView, mAccessibilityDelegate);
}
}
@@ -527,6 +539,46 @@
.setDuration(DRAG_LENGTH)
.alpha(.6f);
}
+
+ boolean canRemove() {
+ return canRemoveFromPosition(getLayoutPosition());
+ }
+
+ boolean canAdd() {
+ return canAddFromPosition(getLayoutPosition());
+ }
+
+ void toggleState() {
+ if (canAdd()) {
+ add();
+ } else {
+ remove();
+ }
+ }
+
+ private void add() {
+ addFromPosition(getLayoutPosition());
+ }
+
+ private void remove() {
+ removeFromPosition(getLayoutPosition());
+ }
+
+ boolean isCurrentTile() {
+ return TileAdapter.this.isCurrentTile(getLayoutPosition());
+ }
+
+ void startAccessibleAdd() {
+ TileAdapter.this.startAccessibleAdd(getLayoutPosition());
+ }
+
+ void startAccessibleMove() {
+ TileAdapter.this.startAccessibleMove(getLayoutPosition());
+ }
+
+ boolean canTakeAccessibleAction() {
+ return mAccessibilityAction == ACTION_NONE;
+ }
}
private final SpanSizeLookup mSizeLookup = new SpanSizeLookup() {
@@ -648,7 +700,7 @@
to == 0 || to == RecyclerView.NO_POSITION) {
return false;
}
- return move(from, to, target.itemView);
+ return move(from, to);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapterDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapterDelegate.java
new file mode 100644
index 0000000..1e426ad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapterDelegate.java
@@ -0,0 +1,152 @@
+/*
+ * 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.customize;
+
+import android.os.Bundle;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import androidx.core.view.AccessibilityDelegateCompat;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+
+import com.android.systemui.R;
+
+import java.util.List;
+
+/**
+ * Accessibility delegate for {@link TileAdapter} views.
+ *
+ * This delegate will populate the accessibility info with the proper actions that can be taken for
+ * the different tiles:
+ * <ul>
+ * <li>Add to end if the tile is not a current tile (by double tap).</li>
+ * <li>Add to a given position (by context menu). This will let the user select a position.</li>
+ * <li>Remove, if the tile is a current tile (by double tap).</li>
+ * <li>Move to a given position (by context menu). This will let the user select a position.</li>
+ * </ul>
+ *
+ * This only handles generating the associated actions. The logic for selecting positions is handled
+ * by {@link TileAdapter}.
+ *
+ * In order for the delegate to work properly, the asociated {@link TileAdapter.Holder} should be
+ * passed along with the view using {@link View#setTag}.
+ */
+class TileAdapterDelegate extends AccessibilityDelegateCompat {
+
+ private static final int MOVE_TO_POSITION_ID = R.id.accessibility_action_qs_move_to_position;
+ private static final int ADD_TO_POSITION_ID = R.id.accessibility_action_qs_add_to_position;
+
+ private TileAdapter.Holder getHolder(View view) {
+ return (TileAdapter.Holder) view.getTag();
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ TileAdapter.Holder holder = getHolder(host);
+ info.setCollectionItemInfo(null);
+ info.setStateDescription("");
+ if (holder == null || !holder.canTakeAccessibleAction()) {
+ // If there's not a holder (not a regular Tile) or an action cannot be taken
+ // because we are in the middle of an accessibility action, don't create a special node.
+ return;
+ }
+
+ addClickAction(host, info, holder);
+ maybeAddActionAddToPosition(host, info, holder);
+ maybeAddActionMoveToPosition(host, info, holder);
+
+ if (holder.isCurrentTile()) {
+ info.setStateDescription(host.getContext().getString(
+ R.string.accessibility_qs_edit_position, holder.getLayoutPosition()));
+ }
+ }
+
+ @Override
+ public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ TileAdapter.Holder holder = getHolder(host);
+
+ if (holder == null || !holder.canTakeAccessibleAction()) {
+ // If there's not a holder (not a regular Tile) or an action cannot be taken
+ // because we are in the middle of an accessibility action, perform the default action.
+ return super.performAccessibilityAction(host, action, args);
+ }
+ if (action == AccessibilityNodeInfo.ACTION_CLICK) {
+ holder.toggleState();
+ return true;
+ } else if (action == MOVE_TO_POSITION_ID) {
+ holder.startAccessibleMove();
+ return true;
+ } else if (action == ADD_TO_POSITION_ID) {
+ holder.startAccessibleAdd();
+ return true;
+ } else {
+ return super.performAccessibilityAction(host, action, args);
+ }
+ }
+
+ private void addClickAction(
+ View host, AccessibilityNodeInfoCompat info, TileAdapter.Holder holder) {
+ String clickActionString;
+ if (holder.canAdd()) {
+ clickActionString = host.getContext().getString(
+ R.string.accessibility_qs_edit_tile_add_action);
+ } else if (holder.canRemove()) {
+ clickActionString = host.getContext().getString(
+ R.string.accessibility_qs_edit_remove_tile_action);
+ } else {
+ // Remove the default click action if tile can't either be added or removed (for example
+ // if there's the minimum number of tiles)
+ List<AccessibilityNodeInfoCompat.AccessibilityActionCompat> listOfActions =
+ info.getActionList(); // This is a copy
+ int numActions = listOfActions.size();
+ for (int i = 0; i < numActions; i++) {
+ if (listOfActions.get(i).getId() == AccessibilityNodeInfo.ACTION_CLICK) {
+ info.removeAction(listOfActions.get(i));
+ }
+ }
+ return;
+ }
+
+ AccessibilityNodeInfoCompat.AccessibilityActionCompat action =
+ new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+ AccessibilityNodeInfo.ACTION_CLICK, clickActionString);
+ info.addAction(action);
+ }
+
+ private void maybeAddActionMoveToPosition(
+ View host, AccessibilityNodeInfoCompat info, TileAdapter.Holder holder) {
+ if (holder.isCurrentTile()) {
+ AccessibilityNodeInfoCompat.AccessibilityActionCompat action =
+ new AccessibilityNodeInfoCompat.AccessibilityActionCompat(MOVE_TO_POSITION_ID,
+ host.getContext().getString(
+ R.string.accessibility_qs_edit_tile_start_move));
+ info.addAction(action);
+ }
+ }
+
+ private void maybeAddActionAddToPosition(
+ View host, AccessibilityNodeInfoCompat info, TileAdapter.Holder holder) {
+ if (holder.canAdd()) {
+ AccessibilityNodeInfoCompat.AccessibilityActionCompat action =
+ new AccessibilityNodeInfoCompat.AccessibilityActionCompat(ADD_TO_POSITION_ID,
+ host.getContext().getString(
+ R.string.accessibility_qs_edit_tile_start_add));
+ info.addAction(action);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index db77e08..73c6504 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -80,8 +80,6 @@
mFinished = false;
// Enqueue jobs to fetch every system tile and then ever package tile.
addCurrentAndStockTiles(host);
-
- addPackageTiles(host);
}
public boolean isFinished() {
@@ -122,23 +120,86 @@
tile.destroy();
continue;
}
- tile.setListening(this, true);
- tile.refreshState();
- tile.setListening(this, false);
tile.setTileSpec(spec);
tilesToAdd.add(tile);
}
- mBgExecutor.execute(() -> {
- for (QSTile tile : tilesToAdd) {
- final QSTile.State state = tile.getState().copy();
- // Ignore the current state and get the generic label instead.
- state.label = tile.getTileLabel();
- tile.destroy();
- addTile(tile.getTileSpec(), null, state, true);
+ new TileCollector(tilesToAdd, host).startListening();
+ }
+
+ private static class TilePair {
+ QSTile mTile;
+ boolean mReady = false;
+ }
+
+ private class TileCollector implements QSTile.Callback {
+
+ private final List<TilePair> mQSTileList = new ArrayList<>();
+ private final QSTileHost mQSTileHost;
+
+ TileCollector(List<QSTile> tilesToAdd, QSTileHost host) {
+ for (QSTile tile: tilesToAdd) {
+ TilePair pair = new TilePair();
+ pair.mTile = tile;
+ mQSTileList.add(pair);
}
+ mQSTileHost = host;
+ if (tilesToAdd.isEmpty()) {
+ mBgExecutor.execute(this::finished);
+ }
+ }
+
+ private void finished() {
notifyTilesChanged(false);
- });
+ addPackageTiles(mQSTileHost);
+ }
+
+ private void startListening() {
+ for (TilePair pair: mQSTileList) {
+ pair.mTile.addCallback(this);
+ pair.mTile.setListening(this, true);
+ // Make sure that at least one refresh state happens
+ pair.mTile.refreshState();
+ }
+ }
+
+ // This is called in the Bg thread
+ @Override
+ public void onStateChanged(State s) {
+ boolean allReady = true;
+ for (TilePair pair: mQSTileList) {
+ if (!pair.mReady && pair.mTile.isTileReady()) {
+ pair.mTile.removeCallback(this);
+ pair.mTile.setListening(this, false);
+ pair.mReady = true;
+ } else if (!pair.mReady) {
+ allReady = false;
+ }
+ }
+ if (allReady) {
+ for (TilePair pair : mQSTileList) {
+ QSTile tile = pair.mTile;
+ final QSTile.State state = tile.getState().copy();
+ // Ignore the current state and get the generic label instead.
+ state.label = tile.getTileLabel();
+ tile.destroy();
+ addTile(tile.getTileSpec(), null, state, true);
+ }
+ finished();
+ }
+ }
+
+ @Override
+ public void onShowDetail(boolean show) {}
+
+ @Override
+ public void onToggleStateChanged(boolean state) {}
+
+ @Override
+ public void onScanStateChanged(boolean state) {}
+
+ @Override
+ public void onAnnouncementRequested(CharSequence announcement) {}
}
private void addPackageTiles(final QSTileHost host) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 255513a..dfd7e2c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -90,6 +90,10 @@
private static final long DEFAULT_STALE_TIMEOUT = 10 * DateUtils.MINUTE_IN_MILLIS;
protected static final Object ARG_SHOW_TRANSIENT_ENABLING = new Object();
+ private static final int READY_STATE_NOT_READY = 0;
+ private static final int READY_STATE_READYING = 1;
+ private static final int READY_STATE_READY = 2;
+
protected final QSHost mHost;
protected final Context mContext;
// @NonFinalForTesting
@@ -101,6 +105,7 @@
protected final ActivityStarter mActivityStarter;
private final UiEventLogger mUiEventLogger;
private final QSLogger mQSLogger;
+ private volatile int mReadyState;
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
private final Object mStaleListener = new Object();
@@ -386,7 +391,11 @@
protected void handleRefreshState(Object arg) {
handleUpdateState(mTmpState, arg);
- final boolean changed = mTmpState.copyTo(mState);
+ boolean changed = mTmpState.copyTo(mState);
+ if (mReadyState == READY_STATE_READYING) {
+ mReadyState = READY_STATE_READY;
+ changed = true;
+ }
if (changed) {
mQSLogger.logTileUpdated(mTileSpec, mState);
handleStateChanged();
@@ -459,6 +468,9 @@
// should not refresh it anymore.
if (mLifecycle.getCurrentState().equals(DESTROYED)) return;
mLifecycle.setCurrentState(RESUMED);
+ if (mReadyState == READY_STATE_NOT_READY) {
+ mReadyState = READY_STATE_READYING;
+ }
refreshState(); // Ensure we get at least one refresh after listening.
});
}
@@ -531,6 +543,15 @@
*/
public abstract CharSequence getTileLabel();
+ /**
+ * @return {@code true} if the tile has refreshed state at least once after having set its
+ * lifecycle to {@link Lifecycle.State#RESUMED}.
+ */
+ @Override
+ public boolean isTileReady() {
+ return mReadyState == READY_STATE_READY;
+ }
+
public static int getColorForState(Context context, int state) {
switch (state) {
case Tile.STATE_UNAVAILABLE:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 4673ec7..37ae791 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -44,6 +44,8 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.view.InsetsState.InternalInsetsType;
@@ -59,7 +61,9 @@
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.tracing.ProtoTracer;
+import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
/**
* This class takes the functions from IStatusBar that come in on
@@ -70,6 +74,8 @@
*/
public class CommandQueue extends IStatusBar.Stub implements CallbackController<Callbacks>,
DisplayManager.DisplayListener {
+ private static final String TAG = CommandQueue.class.getSimpleName();
+
private static final int INDEX_MASK = 0xffff;
private static final int MSG_SHIFT = 16;
private static final int MSG_MASK = 0xffff << MSG_SHIFT;
@@ -131,6 +137,7 @@
private static final int MSG_TRACING_STATE_CHANGED = 55 << MSG_SHIFT;
private static final int MSG_SUPPRESS_AMBIENT_DISPLAY = 56 << MSG_SHIFT;
private static final int MSG_REQUEST_WINDOW_MAGNIFICATION_CONNECTION = 57 << MSG_SHIFT;
+ private static final int MSG_HANDLE_WINDOW_MANAGER_LOGGING_COMMAND = 58 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -353,6 +360,11 @@
* @param connect {@code true} if needs connection, otherwise set the connection to null.
*/
default void requestWindowMagnificationConnection(boolean connect) { }
+
+ /**
+ * Handles a window manager shell logging command.
+ */
+ default void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd) {}
}
public CommandQueue(Context context) {
@@ -984,6 +996,17 @@
}
@Override
+ public void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd) {
+ synchronized (mLock) {
+ SomeArgs internalArgs = SomeArgs.obtain();
+ internalArgs.arg1 = args;
+ internalArgs.arg2 = outFd;
+ mHandler.obtainMessage(MSG_HANDLE_WINDOW_MANAGER_LOGGING_COMMAND, internalArgs)
+ .sendToTarget();
+ }
+ }
+
+ @Override
public void suppressAmbientDisplay(boolean suppress) {
synchronized (mLock) {
mHandler.obtainMessage(MSG_SUPPRESS_AMBIENT_DISPLAY, suppress).sendToTarget();
@@ -1334,6 +1357,18 @@
mCallbacks.get(i).requestWindowMagnificationConnection((Boolean) msg.obj);
}
break;
+ case MSG_HANDLE_WINDOW_MANAGER_LOGGING_COMMAND:
+ args = (SomeArgs) msg.obj;
+ try (ParcelFileDescriptor pfd = (ParcelFileDescriptor) args.arg2) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).handleWindowManagerLoggingCommand(
+ (String[]) args.arg1, pfd);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to handle logging command", e);
+ }
+ args.recycle();
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 2327063..01333f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -23,7 +23,6 @@
import android.os.Handler;
import android.view.accessibility.AccessibilityManager;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.R;
@@ -68,7 +67,6 @@
import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl;
import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController;
-import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogController;
@@ -198,20 +196,6 @@
return new NotificationPanelLoggerImpl();
}
- /** Provides an instance of {@link NotificationBlockingHelperManager} */
- @SysUISingleton
- @Provides
- static NotificationBlockingHelperManager provideNotificationBlockingHelperManager(
- Context context,
- NotificationGutsManager notificationGutsManager,
- NotificationEntryManager notificationEntryManager,
- MetricsLogger metricsLogger,
- GroupMembershipManager groupMembershipManager) {
- return new NotificationBlockingHelperManager(
- context, notificationGutsManager, notificationEntryManager, metricsLogger,
- groupMembershipManager);
- }
-
/** Provides an instance of {@link GroupMembershipManager} */
@SysUISingleton
@Provides
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 adda049..811a72d 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
@@ -500,10 +500,6 @@
* or is in a whitelist).
*/
public boolean getIsNonblockable() {
- boolean isNonblockable = Dependency.get(NotificationBlockingHelperManager.class)
- .isNonblockable(mEntry.getSbn().getPackageName(),
- mEntry.getChannel().getId());
-
// If the SystemNotifAsyncTask hasn't finished running or retrieved a value, we'll try once
// again, but in-place on the main thread this time. This should rarely ever get called.
if (mEntry != null && mEntry.mIsSystemNotification == null) {
@@ -514,13 +510,12 @@
mEntry.mIsSystemNotification = isSystemNotification(mContext, mEntry.getSbn());
}
- isNonblockable |= mEntry.getChannel().isImportanceLockedByOEM();
- isNonblockable |= mEntry.getChannel().isImportanceLockedByCriticalDeviceFunction();
+ boolean isNonblockable = mEntry.getChannel().isImportanceLockedByOEM()
+ || mEntry.getChannel().isImportanceLockedByCriticalDeviceFunction();
if (!isNonblockable && mEntry != null && mEntry.mIsSystemNotification != null) {
if (mEntry.mIsSystemNotification) {
- if (mEntry.getChannel() != null
- && !mEntry.getChannel().isBlockable()) {
+ if (mEntry.getChannel() != null && !mEntry.getChannel().isBlockable()) {
isNonblockable = true;
}
}
@@ -1398,26 +1393,12 @@
}
/**
- * Dismisses the notification with the option of showing the blocking helper in-place if we have
- * a negative user sentiment.
+ * Dismisses the notification.
*
* @param fromAccessibility whether this dismiss is coming from an accessibility action
- * @return whether a blocking helper is shown in this row
*/
- public boolean performDismissWithBlockingHelper(boolean fromAccessibility) {
- NotificationBlockingHelperManager manager =
- Dependency.get(NotificationBlockingHelperManager.class);
- boolean isBlockingHelperShown = manager.perhapsShowBlockingHelper(this, mMenuRow);
-
- Dependency.get(MetricsLogger.class).count(NotificationCounters.NOTIFICATION_DISMISSED, 1);
-
- // Continue with dismiss since we don't want the blocking helper to be directly associated
- // with a certain notification.
- performDismiss(fromAccessibility);
- return isBlockingHelperShown;
- }
-
public void performDismiss(boolean fromAccessibility) {
+ Dependency.get(MetricsLogger.class).count(NotificationCounters.NOTIFICATION_DISMISSED, 1);
dismiss(fromAccessibility);
if (mEntry.isClearable()) {
if (mOnUserInteractionCallback != null) {
@@ -2983,7 +2964,7 @@
}
switch (action) {
case AccessibilityNodeInfo.ACTION_DISMISS:
- performDismissWithBlockingHelper(true /* fromAccessibility */);
+ performDismiss(true /* fromAccessibility */);
return true;
case AccessibilityNodeInfo.ACTION_COLLAPSE:
case AccessibilityNodeInfo.ACTION_EXPAND:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 7c7bb5c..b19997d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -42,6 +42,8 @@
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -538,12 +540,21 @@
&& Settings.Global.getInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, 0) == 1;
+ Drawable person = mIconFactory.getBaseIconDrawable(mShortcutInfo);
+ if (person == null) {
+ person = mContext.getDrawable(R.drawable.ic_person).mutate();
+ TypedArray ta = mContext.obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
+ int colorAccent = ta.getColor(0, 0);
+ ta.recycle();
+ person.setTint(colorAccent);
+ }
+
PriorityOnboardingDialogController controller = mBuilderProvider.get()
.setContext(mUserContext)
.setView(onboardingView)
.setIgnoresDnd(ignoreDnd)
.setShowsAsBubble(showAsBubble)
- .setIcon(mIconFactory.getBaseIconDrawable(mShortcutInfo))
+ .setIcon(person)
.setBadge(mIconFactory.getAppBadge(
mPackageName, UserHandle.getUserId(mSbn.getUid())))
.setOnSettingsClick(mOnConversationSettingsClickListener)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
index eeac46a..7071b73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
@@ -295,10 +295,6 @@
*/
private void closeControls(int x, int y, boolean save, boolean force) {
// First try to dismiss any blocking helper.
- boolean wasBlockingHelperDismissed =
- Dependency.get(NotificationBlockingHelperManager.class)
- .dismissCurrentBlockingHelper();
-
if (getWindowToken() == null) {
if (mClosedListener != null) {
mClosedListener.onGutsClosed(this);
@@ -307,10 +303,9 @@
}
if (mGutsContent == null
- || !mGutsContent.handleCloseControls(save, force)
- || wasBlockingHelperDismissed) {
+ || !mGutsContent.handleCloseControls(save, force)) {
// We only want to do a circular reveal if we're not showing the blocking helper.
- animateClose(x, y, !wasBlockingHelperDismissed /* shouldDoCircularReveal */);
+ animateClose(x, y, true /* shouldDoCircularReveal */);
setExposed(false, mNeedsFalsingProtection);
if (mClosedListener != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index fe66669..f9d7c1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -24,6 +24,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.util.HashSet;
@@ -34,7 +35,7 @@
* A class that manages the roundness for notification views
*/
@SysUISingleton
-public class NotificationRoundnessManager implements OnHeadsUpChangedListener {
+public class NotificationRoundnessManager {
private final ExpandableView[] mFirstInSectionViews;
private final ExpandableView[] mLastInSectionViews;
@@ -59,27 +60,7 @@
mBypassController = keyguardBypassController;
}
- @Override
- public void onHeadsUpPinned(NotificationEntry headsUp) {
- updateView(headsUp.getRow(), false /* animate */);
- }
-
- @Override
- public void onHeadsUpUnPinned(NotificationEntry headsUp) {
- updateView(headsUp.getRow(), true /* animate */);
- }
-
- public void onHeadsupAnimatingAwayChanged(ExpandableNotificationRow row,
- boolean isAnimatingAway) {
- updateView(row, false /* animate */);
- }
-
- @Override
- public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
- updateView(entry.getRow(), false /* animate */);
- }
-
- private void updateView(ExpandableView view, boolean animate) {
+ public void updateView(ExpandableView view, boolean animate) {
boolean changed = updateViewWithoutCallback(view, animate);
if (changed) {
mRoundingChangedCallback.run();
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 e0e3013..500de2d 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
@@ -73,9 +73,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEvent;
-import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.keyguard.KeyguardSliceView;
import com.android.settingslib.Utils;
@@ -95,7 +92,6 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.FakeShadowView;
-import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.ShadeViewRefactor;
@@ -110,7 +106,6 @@
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.FooterView;
import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
-import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
@@ -264,6 +259,7 @@
protected EmptyShadeView mEmptyShadeView;
private boolean mDismissAllInProgress;
private boolean mFadeNotificationsOnDismiss;
+ private FooterDismissListener mFooterDismissListener;
/**
* Was the scroller scrolled to the top when the down motion was observed?
@@ -318,7 +314,6 @@
private HashSet<ExpandableView> mClearTransientViewsWhenFinished = new HashSet<>();
private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
= new HashSet<>();
- private final NotificationRoundnessManager mRoundnessManager;
private boolean mTrackingHeadsUp;
private boolean mForceNoOverlappingRendering;
private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
@@ -450,25 +445,20 @@
private int mHeadsUpInset;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private final Rect mTmpRect = new Rect();
+ private DismissListener mDismissListener;
private DismissAllAnimationListener mDismissAllAnimationListener;
- @VisibleForTesting
- protected final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
- protected final UiEventLogger mUiEventLogger;
- private final NotificationRemoteInputManager mRemoteInputManager =
- Dependency.get(NotificationRemoteInputManager.class);
+ private NotificationRemoteInputManager mRemoteInputManager;
+ private ShadeController mShadeController;
private final DisplayMetrics mDisplayMetrics = Dependency.get(DisplayMetrics.class);
private final LockscreenGestureLogger mLockscreenGestureLogger =
Dependency.get(LockscreenGestureLogger.class);
- private final VisualStabilityManager mVisualStabilityManager =
- Dependency.get(VisualStabilityManager.class);
protected boolean mClearAllEnabled;
private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
private NotificationPanelViewController mNotificationPanelController;
private final NotificationSectionsManager mSectionsManager;
- private final ForegroundServiceSectionController mFgsSectionController;
private ForegroundServiceDungeonView mFgsSectionView;
private boolean mAnimateBottomOnLayout;
private float mLastSentAppear;
@@ -529,22 +519,15 @@
public NotificationStackScrollLayout(
@Named(VIEW_CONTEXT) Context context,
AttributeSet attrs,
- NotificationRoundnessManager notificationRoundnessManager,
- SysuiStatusBarStateController statusbarStateController,
NotificationSectionsManager notificationSectionsManager,
- ForegroundServiceSectionController fgsSectionController,
- ForegroundServiceDismissalFeatureController fgsFeatureController,
GroupMembershipManager groupMembershipManager,
GroupExpansionManager groupExpansionManager,
- UiEventLogger uiEventLogger
+ SysuiStatusBarStateController statusbarStateController
) {
super(context, attrs, 0, 0);
Resources res = getResources();
-
- mRoundnessManager = notificationRoundnessManager;
- mFgsSectionController = fgsSectionController;
-
mSectionsManager = notificationSectionsManager;
+
mSectionsManager.initialize(this, LayoutInflater.from(context));
mSectionsManager.setOnClearSilentNotifsClickListener(v -> {
// Leave the shade open if there will be other notifs left over to clear
@@ -568,13 +551,6 @@
res.getBoolean(R.bool.config_drawNotificationBackground);
setOutlineProvider(mOutlineProvider);
- // Blocking helper manager wants to know the expanded state, update as well.
- NotificationBlockingHelperManager blockingHelperManager =
- Dependency.get(NotificationBlockingHelperManager.class);
- addOnExpandedHeightChangedListener((height, unused) -> {
- blockingHelperManager.setNotificationShadeExpanded(height);
- });
-
boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
setWillNotDraw(!willDraw);
mBackgroundPaint.setAntiAlias(true);
@@ -589,18 +565,14 @@
mGroupMembershipManager = groupMembershipManager;
mGroupExpansionManager = groupExpansionManager;
mStatusbarStateController = statusbarStateController;
- initializeForegroundServiceSection(fgsFeatureController);
- mUiEventLogger = uiEventLogger;
}
- private void initializeForegroundServiceSection(
- ForegroundServiceDismissalFeatureController featureController) {
- if (featureController.isForegroundServiceDismissalEnabled()) {
- LayoutInflater li = LayoutInflater.from(mContext);
- mFgsSectionView =
- (ForegroundServiceDungeonView) mFgsSectionController.createView(li);
- addView(mFgsSectionView, -1);
+ void initializeForegroundServiceSection(ForegroundServiceDungeonView fgsSectionView) {
+ if (mFgsSectionView != null) {
+ return;
}
+ mFgsSectionView = fgsSectionView;
+ addView(mFgsSectionView, -1);
}
void updateDismissRtlSetting(boolean dismissRtl) {
@@ -620,7 +592,6 @@
inflateEmptyShadeView();
inflateFooterView();
- mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation);
}
/**
@@ -1038,23 +1009,6 @@
}
@ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
- public boolean isInVisibleLocation(NotificationEntry entry) {
- ExpandableNotificationRow row = entry.getRow();
- ExpandableViewState childViewState = row.getViewState();
-
- if (childViewState == null) {
- return false;
- }
- if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
- return false;
- }
- if (row.getVisibility() != View.VISIBLE) {
- return false;
- }
- return true;
- }
-
- @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
private void setMaxLayoutHeight(int maxLayoutHeight) {
mMaxLayoutHeight = maxLayoutHeight;
mShelf.setMaxLayoutHeight(maxLayoutHeight);
@@ -3222,7 +3176,8 @@
mAnimateNextSectionBoundsChange = false;
}
mAmbientState.setLastVisibleBackgroundChild(lastChild);
- mRoundnessManager.updateRoundedChildren(mSections);
+ // TODO: Refactor SectionManager and put the RoundnessManager there.
+ mController.getNoticationRoundessManager().updateRoundedChildren(mSections);
mAnimateBottomOnLayout = false;
invalidate();
}
@@ -3297,14 +3252,6 @@
requestChildrenUpdate();
}
- @ShadeViewRefactor(RefactorComponent.ADAPTER)
- void bindRow(ExpandableNotificationRow row) {
- row.setHeadsUpAnimatingAwayListener(animatingAway -> {
- mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway);
- mHeadsUpAppearanceController.updateHeader(row.getEntry());
- });
- }
-
public boolean containsView(View v) {
return v.getParent() == this;
}
@@ -5022,7 +4969,6 @@
public void setTrackingHeadsUp(ExpandableNotificationRow row) {
mAmbientState.setTrackedHeadsUpRow(row);
mTrackingHeadsUp = row != null;
- mRoundnessManager.setTrackingHeadsUp(row);
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -5343,16 +5289,14 @@
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setHeadsUpAppearanceController(
+ void setHeadsUpAppearanceController(
HeadsUpAppearanceController headsUpAppearanceController) {
mHeadsUpAppearanceController = headsUpAppearanceController;
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@VisibleForTesting
- void clearNotifications(
- @SelectedRows int selection,
- boolean closeShade) {
+ void clearNotifications(@SelectedRows int selection, boolean closeShade) {
// animate-swipe all dismissable notifications, then animate the shade closed
int numChildren = getChildCount();
@@ -5393,13 +5337,13 @@
}
}
- // Log dismiss event even if there's nothing to dismiss
- mUiEventLogger.log(NotificationPanelEvent.fromSelection(selection));
+ if (mDismissListener != null) {
+ mDismissListener.onDismiss(selection);
+ }
if (viewsToRemove.isEmpty()) {
- if (closeShade) {
- Dependency.get(ShadeController.class).animateCollapsePanels(
- CommandQueue.FLAG_EXCLUDE_NONE);
+ if (closeShade && mShadeController != null) {
+ mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
}
return;
}
@@ -5435,11 +5379,11 @@
final Runnable onSlideAwayAnimationComplete = () -> {
if (closeShade) {
- Dependency.get(ShadeController.class).addPostCollapseAction(() -> {
+ mShadeController.addPostCollapseAction(() -> {
setDismissAllInProgress(false);
onAnimationComplete.run();
});
- Dependency.get(ShadeController.class).animateCollapsePanels(
+ mShadeController.animateCollapsePanels(
CommandQueue.FLAG_EXCLUDE_NONE);
} else {
setDismissAllInProgress(false);
@@ -5484,7 +5428,9 @@
FooterView footerView = (FooterView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_notification_footer, this, false);
footerView.setDismissButtonClickListener(v -> {
- mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES);
+ if (mFooterDismissListener != null) {
+ mFooterDismissListener.onDismiss();
+ }
clearNotifications(ROWS_ALL, true /* closeShade */);
});
footerView.setManageButtonClickListener(v -> {
@@ -5701,6 +5647,10 @@
return mCheckForLeavebehind;
}
+ void setDismissListener (DismissListener listener) {
+ mDismissListener = listener;
+ }
+
void setDismissAllAnimationListener(DismissAllAnimationListener dismissAllAnimationListener) {
mDismissAllAnimationListener = dismissAllAnimationListener;
}
@@ -5709,6 +5659,18 @@
mHighPriorityBeforeSpeedBump = highPriorityBeforeSpeedBump;
}
+ void setFooterDismissListener(FooterDismissListener listener) {
+ mFooterDismissListener = listener;
+ }
+
+ public void setRemoteInputManager(NotificationRemoteInputManager remoteInputManager) {
+ mRemoteInputManager = remoteInputManager;
+ }
+
+ void setShadeController(ShadeController shadeController) {
+ mShadeController = shadeController;
+ }
+
/**
* A listener that is notified when the empty space below the notifications is clicked on
*/
@@ -6108,7 +6070,7 @@
if (!mAmbientState.isDozing() || startingChild != null) {
// We have notifications, go to locked shade.
- Dependency.get(ShadeController.class).goToLockedShade(startingChild);
+ mShadeController.goToLockedShade(startingChild);
if (startingChild instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
row.onExpandedByGesture(true /* drag down is always an open */);
@@ -6312,41 +6274,18 @@
/** Only rows where entry.isHighPriority() is false. */
public static final int ROWS_GENTLE = 2;
- /**
- * Enum for UiEvent logged from this class
- */
- enum NotificationPanelEvent implements UiEventLogger.UiEventEnum {
- INVALID(0),
- @UiEvent(doc = "User dismissed all notifications from notification panel.")
- DISMISS_ALL_NOTIFICATIONS_PANEL(312),
- @UiEvent(doc = "User dismissed all silent notifications from notification panel.")
- DISMISS_SILENT_NOTIFICATIONS_PANEL(314);
- private final int mId;
- NotificationPanelEvent(int id) {
- mId = id;
- }
- @Override public int getId() {
- return mId;
- }
-
- public static UiEventLogger.UiEventEnum fromSelection(@SelectedRows int selection) {
- if (selection == ROWS_ALL) {
- return DISMISS_ALL_NOTIFICATIONS_PANEL;
- }
- if (selection == ROWS_GENTLE) {
- return DISMISS_SILENT_NOTIFICATIONS_PANEL;
- }
- if (NotificationStackScrollLayout.DEBUG) {
- throw new IllegalArgumentException("Unexpected selection" + selection);
- }
- return INVALID;
- }
- }
-
interface KeyguardBypassEnabledProvider {
boolean getBypassEnabled();
}
+ interface DismissListener {
+ void onDismiss(@SelectedRows int selectedRows);
+ }
+
+ interface FooterDismissListener {
+ void onDismiss();
+ }
+
interface DismissAllAnimationListener {
void onAnimationEnd(
List<ExpandableNotificationRow> viewsToRemove, @SelectedRows int selectedRows);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 88b7fab..703c214 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -21,11 +21,11 @@
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnOverscrollTopChangedListener;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.canChildBeDismissed;
import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY;
@@ -39,6 +39,7 @@
import android.util.Log;
import android.util.Pair;
import android.view.Display;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -47,6 +48,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
@@ -65,22 +68,24 @@
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.ShadeViewRefactor;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.NotificationGroup;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener;
+import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
@@ -90,6 +95,7 @@
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationSnooze;
@@ -99,6 +105,7 @@
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -122,6 +129,7 @@
@StatusBarComponent.StatusBarScope
public class NotificationStackScrollLayoutController {
private static final String TAG = "StackScrollerController";
+ private static final boolean DEBUG = false;
private final boolean mAllowLongPress;
private final NotificationGutsManager mNotificationGutsManager;
@@ -133,7 +141,6 @@
private final ZenModeController mZenModeController;
private final MetricsLogger mMetricsLogger;
private final FalsingManager mFalsingManager;
- private final NotificationSectionsManager mNotificationSectionsManager;
private final Resources mResources;
private final NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
private final ScrimController mScrimController;
@@ -142,6 +149,13 @@
private final NotifCollection mNotifCollection;
private final NotificationEntryManager mNotificationEntryManager;
private final IStatusBarService mIStatusBarService;
+ private final UiEventLogger mUiEventLogger;
+ private final ForegroundServiceDismissalFeatureController mFgFeatureController;
+ private final ForegroundServiceSectionController mFgServicesSectionController;
+ private final LayoutInflater mLayoutInflater;
+ private final NotificationRemoteInputManager mRemoteInputManager;
+ private final VisualStabilityManager mVisualStabilityManager;
+ private final ShadeController mShadeController;
private final KeyguardMediaController mKeyguardMediaController;
private final SysuiStatusBarStateController mStatusBarStateController;
private final KeyguardBypassController mKeyguardBypassController;
@@ -156,6 +170,7 @@
private NotificationSwipeHelper mSwipeHelper;
private boolean mShowEmptyShadeView;
private int mBarState;
+ private HeadsUpAppearanceController mHeadsUpAppearanceController;
private final NotificationListContainerImpl mNotificationListContainer =
new NotificationListContainerImpl();
@@ -377,8 +392,6 @@
return;
}
- boolean isBlockingHelperShown = false;
-
mView.removeDraggedView(view);
mView.updateContinuousShadowDrawing();
@@ -388,13 +401,10 @@
mHeadsUpManager.addSwipedOutNotification(
row.getEntry().getSbn().getKey());
}
- isBlockingHelperShown =
- row.performDismissWithBlockingHelper(false /* fromAccessibility */);
+ row.performDismiss(false /* fromAccessibility */);
}
- if (!isBlockingHelperShown) {
- mView.addSwipedOutView(view);
- }
+ mView.addSwipedOutView(view);
mFalsingManager.onNotificationDismissed();
if (mFalsingManager.shouldEnforceBouncer()) {
mStatusBar.executeRunnableDismissingKeyguard(
@@ -497,29 +507,30 @@
private final OnHeadsUpChangedListener mOnHeadsUpChangedListener =
new OnHeadsUpChangedListener() {
- @Override
- public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
- mView.setInHeadsUpPinnedMode(inPinnedMode);
- }
+ @Override
+ public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
+ mView.setInHeadsUpPinnedMode(inPinnedMode);
+ }
- @Override
- public void onHeadsUpPinned(NotificationEntry entry) {
+ @Override
+ public void onHeadsUpPinned(NotificationEntry entry) {
+ mNotificationRoundnessManager.updateView(entry.getRow(), false /* animate */);
+ }
- }
+ @Override
+ public void onHeadsUpUnPinned(NotificationEntry entry) {
+ mNotificationRoundnessManager.updateView(entry.getRow(), true /* animate */);
+ }
- @Override
- public void onHeadsUpUnPinned(NotificationEntry entry) {
-
- }
-
- @Override
- public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
- long numEntries = mHeadsUpManager.getAllEntries().count();
- NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
- mView.setNumHeadsUp(numEntries);
- mView.setTopHeadsUpEntry(topEntry);
- }
- };
+ @Override
+ public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
+ long numEntries = mHeadsUpManager.getAllEntries().count();
+ NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
+ mView.setNumHeadsUp(numEntries);
+ mView.setTopHeadsUpEntry(topEntry);
+ mNotificationRoundnessManager.updateView(entry.getRow(), false /* animate */);
+ }
+ };
private final ZenModeController.Callback mZenModeControllerCallback =
new ZenModeController.Callback() {
@@ -546,7 +557,6 @@
NotificationLockscreenUserManager lockscreenUserManager,
MetricsLogger metricsLogger,
FalsingManager falsingManager,
- NotificationSectionsManager notificationSectionsManager,
@Main Resources resources,
NotificationSwipeHelper.Builder notificationSwipeHelperBuilder,
StatusBar statusBar,
@@ -558,7 +568,14 @@
NotifPipeline notifPipeline,
NotifCollection notifCollection,
NotificationEntryManager notificationEntryManager,
- IStatusBarService iStatusBarService) {
+ IStatusBarService iStatusBarService,
+ UiEventLogger uiEventLogger,
+ ForegroundServiceDismissalFeatureController fgFeatureController,
+ ForegroundServiceSectionController fgServicesSectionController,
+ LayoutInflater layoutInflater,
+ NotificationRemoteInputManager remoteInputManager,
+ VisualStabilityManager visualStabilityManager,
+ ShadeController shadeController) {
mAllowLongPress = allowLongPress;
mNotificationGutsManager = notificationGutsManager;
mHeadsUpManager = headsUpManager;
@@ -574,14 +591,12 @@
mLockscreenUserManager = lockscreenUserManager;
mMetricsLogger = metricsLogger;
mFalsingManager = falsingManager;
- mNotificationSectionsManager = notificationSectionsManager;
mResources = resources;
mNotificationSwipeHelperBuilder = notificationSwipeHelperBuilder;
mStatusBar = statusBar;
mScrimController = scrimController;
- groupManager.registerGroupExpansionChangeListener((changedRow, expanded) -> {
- mView.onGroupExpandChanged(changedRow, expanded);
- });
+ groupManager.registerGroupExpansionChangeListener(
+ (changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded));
legacyGroupManager.registerGroupChangeListener(new OnGroupChangeListener() {
@Override
public void onGroupCreatedFromChildren(NotificationGroup group) {
@@ -599,6 +614,13 @@
mNotifCollection = notifCollection;
mNotificationEntryManager = notificationEntryManager;
mIStatusBarService = iStatusBarService;
+ mUiEventLogger = uiEventLogger;
+ mFgFeatureController = fgFeatureController;
+ mFgServicesSectionController = fgServicesSectionController;
+ mLayoutInflater = layoutInflater;
+ mRemoteInputManager = remoteInputManager;
+ mVisualStabilityManager = visualStabilityManager;
+ mShadeController = shadeController;
}
public void attach(NotificationStackScrollLayout view) {
@@ -607,6 +629,18 @@
mView.setTouchHandler(new TouchHandler());
mView.setStatusBar(mStatusBar);
mView.setDismissAllAnimationListener(this::onAnimationEnd);
+ mView.setDismissListener((selection) -> mUiEventLogger.log(
+ NotificationPanelEvent.fromSelection(selection)));
+ mView.setFooterDismissListener(() ->
+ mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES));
+ mView.setRemoteInputManager(mRemoteInputManager);
+ mView.setShadeController(mShadeController);
+
+ if (mFgFeatureController.isForegroundServiceDismissalEnabled()) {
+ mView.initializeForegroundServiceSection(
+ (ForegroundServiceDungeonView) mFgServicesSectionController.createView(
+ mLayoutInflater));
+ }
mSwipeHelper = mNotificationSwipeHelperBuilder
.setSwipeDirection(SwipeHelper.X)
@@ -633,7 +667,6 @@
mView.initView(mView.getContext(), mKeyguardBypassController::getBypassEnabled,
mSwipeHelper);
- mHeadsUpManager.addListener(mNotificationRoundnessManager); // TODO: why is this here?
mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed);
mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener);
@@ -648,6 +681,8 @@
mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate);
mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded);
+ mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation);
+
mTunerService.addTunable(
(key, newValue) -> {
switch (key) {
@@ -690,6 +725,22 @@
mSilentHeaderController.setOnClearAllClickListener(v -> clearSilentNotifications());
}
+ private boolean isInVisibleLocation(NotificationEntry entry) {
+ ExpandableNotificationRow row = entry.getRow();
+ ExpandableViewState childViewState = row.getViewState();
+
+ if (childViewState == null) {
+ return false;
+ }
+ if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
+ return false;
+ }
+ if (row.getVisibility() != View.VISIBLE) {
+ return false;
+ }
+ return true;
+ }
+
public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
mView.addOnExpandedHeightChangedListener(listener);
}
@@ -707,6 +758,7 @@
}
public void setHeadsUpAppearanceController(HeadsUpAppearanceController controller) {
+ mHeadsUpAppearanceController = controller;
mView.setHeadsUpAppearanceController(controller);
}
@@ -755,6 +807,7 @@
public void setTrackingHeadsUp(ExpandableNotificationRow expandableNotificationRow) {
mView.setTrackingHeadsUp(expandableNotificationRow);
+ mNotificationRoundnessManager.setTrackingHeadsUp(expandableNotificationRow);
}
public void wakeUpFromPulse() {
@@ -1293,6 +1346,37 @@
return mDynamicPrivacyController.isInLockedDownShade();
}
+ /**
+ * Enum for UiEvent logged from this class
+ */
+ enum NotificationPanelEvent implements UiEventLogger.UiEventEnum {
+ INVALID(0),
+ @UiEvent(doc = "User dismissed all notifications from notification panel.")
+ DISMISS_ALL_NOTIFICATIONS_PANEL(312),
+ @UiEvent(doc = "User dismissed all silent notifications from notification panel.")
+ DISMISS_SILENT_NOTIFICATIONS_PANEL(314);
+ private final int mId;
+ NotificationPanelEvent(int id) {
+ mId = id;
+ }
+ @Override public int getId() {
+ return mId;
+ }
+
+ public static UiEventLogger.UiEventEnum fromSelection(@SelectedRows int selection) {
+ if (selection == ROWS_ALL) {
+ return DISMISS_ALL_NOTIFICATIONS_PANEL;
+ }
+ if (selection == NotificationStackScrollLayout.ROWS_GENTLE) {
+ return DISMISS_SILENT_NOTIFICATIONS_PANEL;
+ }
+ if (NotificationStackScrollLayoutController.DEBUG) {
+ throw new IllegalArgumentException("Unexpected selection" + selection);
+ }
+ return INVALID;
+ }
+ }
+
private class NotificationListContainerImpl implements NotificationListContainer {
@Override
@@ -1393,7 +1477,7 @@
@Override
public boolean isInVisibleLocation(NotificationEntry entry) {
- return mView.isInVisibleLocation(entry);
+ return NotificationStackScrollLayoutController.this.isInVisibleLocation(entry);
}
@Override
@@ -1408,7 +1492,10 @@
@Override
public void bindRow(ExpandableNotificationRow row) {
- mView.bindRow(row);
+ row.setHeadsUpAnimatingAwayListener(animatingAway -> {
+ mNotificationRoundnessManager.updateView(row, false);
+ mHeadsUpAppearanceController.updateHeader(row.getEntry());
+ });
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
index 11ceedf..77ae059 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
@@ -502,7 +502,7 @@
* @return true if the visibility changed
*/
private boolean updateIconVisibility() {
- boolean onAodOrDocked = mStatusBarStateController.isDozing() && mDocked;
+ boolean onAodOrDocked = mStatusBarStateController.isDozing() || mDocked;
boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance;
boolean fingerprintOrBypass = mFingerprintUnlock
|| mKeyguardBypassController.getBypassEnabled();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java
new file mode 100644
index 0000000..7a78c15
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tv;
+
+import android.Manifest;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.systemui.SystemUI;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.CommandQueue;
+
+import javax.inject.Inject;
+
+/**
+ * Offers control methods for the notification panel handler on TV devices.
+ */
+@SysUISingleton
+public class TvNotificationPanel extends SystemUI implements CommandQueue.Callbacks {
+ private static final String TAG = "TvNotificationPanel";
+ private final CommandQueue mCommandQueue;
+ private final String mNotificationHandlerPackage;
+
+ @Inject
+ public TvNotificationPanel(Context context, CommandQueue commandQueue) {
+ super(context);
+ mCommandQueue = commandQueue;
+ mNotificationHandlerPackage = mContext.getResources().getString(
+ com.android.internal.R.string.config_notificationHandlerPackage);
+ }
+
+ @Override
+ public void start() {
+ mCommandQueue.addCallback(this);
+ }
+
+ @Override
+ public void togglePanel() {
+ if (!mNotificationHandlerPackage.isEmpty()) {
+ startNotificationHandlerActivity(
+ new Intent(NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL));
+ }
+ }
+
+ @Override
+ public void animateExpandNotificationsPanel() {
+ if (!mNotificationHandlerPackage.isEmpty()) {
+ startNotificationHandlerActivity(
+ new Intent(NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL));
+ }
+ }
+
+ @Override
+ public void animateCollapsePanels(int flags, boolean force) {
+ if (!mNotificationHandlerPackage.isEmpty()
+ && (flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) {
+ Intent closeNotificationIntent = new Intent(
+ NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL);
+ closeNotificationIntent.setPackage(mNotificationHandlerPackage);
+ mContext.sendBroadcastAsUser(closeNotificationIntent, UserHandle.CURRENT);
+ }
+ }
+
+ /**
+ * Starts the activity intent if all of the following are true
+ * <ul>
+ * <li> the notification handler package is a system component </li>
+ * <li> the provided intent is handled by the notification handler package </li>
+ * <li> the notification handler requests the
+ * {@link android.Manifest.permission#STATUS_BAR_SERVICE} permission for the given intent</li>
+ * </ul>
+ *
+ * @param intent The intent for starting the desired activity
+ */
+ private void startNotificationHandlerActivity(Intent intent) {
+ intent.setPackage(mNotificationHandlerPackage);
+ PackageManager pm = mContext.getPackageManager();
+ ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_SYSTEM_ONLY);
+ if (ri != null && ri.activityInfo != null) {
+ if (ri.activityInfo.permission != null && ri.activityInfo.permission.equals(
+ Manifest.permission.STATUS_BAR_SERVICE)) {
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ } else {
+ Log.e(TAG,
+ "Not launching notification handler activity: Notification handler does "
+ + "not require the STATUS_BAR_SERVICE permission for intent "
+ + intent.getAction());
+ }
+ } else {
+ Log.e(TAG,
+ "Not launching notification handler activity: Could not resolve activityInfo "
+ + "for intent "
+ + intent.getAction());
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index bcfff60..2795857 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -17,13 +17,9 @@
package com.android.systemui.statusbar.tv;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserHandle;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.R;
@@ -49,9 +45,6 @@
@SysUISingleton
public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks {
- private static final String ACTION_OPEN_TV_NOTIFICATIONS_PANEL =
- "com.android.tv.action.OPEN_NOTIFICATIONS_PANEL";
-
private final CommandQueue mCommandQueue;
private final Lazy<AssistManager> mAssistManagerLazy;
@@ -74,24 +67,11 @@
// If the system process isn't there we're doomed anyway.
}
- if (mContext.getResources().getBoolean(R.bool.audio_recording_disclosure_enabled)) {
+ if (mContext.getResources().getBoolean(R.bool.audio_recording_disclosure_enabled)) {
// Creating AudioRecordingDisclosureBar and just letting it run
new AudioRecordingDisclosureBar(mContext);
}
- }
- @Override
- public void animateExpandNotificationsPanel() {
- startSystemActivity(new Intent(ACTION_OPEN_TV_NOTIFICATIONS_PANEL));
- }
-
- private void startSystemActivity(Intent intent) {
- PackageManager pm = mContext.getPackageManager();
- ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_SYSTEM_ONLY);
- if (ri != null && ri.activityInfo != null) {
- intent.setPackage(ri.activityInfo.packageName);
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
- }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index eb8f065..a6cd350 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -23,7 +23,6 @@
import android.view.LayoutInflater;
import android.view.View;
-import com.android.keyguard.KeyguardMessageArea;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.qs.QSFooterImpl;
import com.android.systemui.qs.QSPanel;
@@ -108,11 +107,6 @@
NotificationStackScrollLayout createNotificationStackScrollLayout();
/**
- * Creates the KeyguardMessageArea.
- */
- KeyguardMessageArea createKeyguardMessageArea();
-
- /**
* Creates the QSPanel.
*/
QSPanel createQSPanel();
diff --git a/packages/SystemUI/src/com/android/systemui/util/ViewController.java b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
index 64f8dbb..c7aa780fc 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
@@ -23,7 +23,20 @@
* Utility class that handles view lifecycle events for View Controllers.
*
* Implementations should handle setup and teardown related activities inside of
- * {@link #onViewAttached()} and {@link #onViewDetached()}.
+ * {@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.
+ *
+ * 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
+ * {@link #onViewAttached()}, the child controller may not have had its onViewAttach method
+ * called, so it may not be fully set up.
+ *
+ * As such, make sure that methods on your controller are safe to call _before_ its {@link #init()}
+ * and {@link #onViewAttached()} methods are called. Specifically, if your controller must call
+ * {@link View#findViewById(int)} on its root view to setup member variables, do so in its
+ * constructor. Save {@link #onViewAttached()} for things that can happen post-construction - adding
+ * listeners, dynamically changing content, or other runtime decisions.
*
* @param <T> View class that this ViewController is for.
*/
@@ -54,10 +67,12 @@
}
mInited = true;
- if (mView.isAttachedToWindow()) {
- mOnAttachStateListener.onViewAttachedToWindow(mView);
+ if (mView != null) {
+ if (mView.isAttachedToWindow()) {
+ mOnAttachStateListener.onViewAttachedToWindow(mView);
+ }
+ mView.addOnAttachStateChangeListener(mOnAttachStateListener);
}
- mView.addOnAttachStateChangeListener(mOnAttachStateListener);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index da7713a..ce125f3 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -28,6 +28,7 @@
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
import android.view.KeyEvent;
import com.android.internal.annotations.VisibleForTesting;
@@ -56,6 +57,7 @@
import com.android.wm.shell.splitscreen.SplitScreen;
import java.io.FileDescriptor;
+import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Optional;
@@ -66,7 +68,8 @@
* Proxy in SysUiScope to delegate events to controllers in WM Shell library.
*/
@SysUISingleton
-public final class WMShell extends SystemUI implements ProtoTraceable<SystemUiTraceProto> {
+public final class WMShell extends SystemUI
+ implements CommandQueue.Callbacks, ProtoTraceable<SystemUiTraceProto> {
private final CommandQueue mCommandQueue;
private final DisplayImeController mDisplayImeController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -100,6 +103,7 @@
ProtoTracer protoTracer) {
super(context);
mCommandQueue = commandQueue;
+ mCommandQueue.addCallback(this);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mActivityManagerWrapper = activityManagerWrapper;
mDisplayImeController = displayImeController;
@@ -293,31 +297,43 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
// Handle commands if provided
- for (int i = 0; i < args.length; i++) {
- switch (args[i]) {
- case "enable-text-logging": {
- String[] groups = Arrays.copyOfRange(args, i + 1, args.length);
- startTextLogging(groups);
- pw.println("Starting logging on groups: " + Arrays.toString(groups));
- return;
- }
- case "disable-text-logging": {
- String[] groups = Arrays.copyOfRange(args, i + 1, args.length);
- stopTextLogging(groups);
- pw.println("Stopping logging on groups: " + Arrays.toString(groups));
- return;
- }
- }
+ if (handleLoggingCommand(args, pw)) {
+ return;
}
// Dump WMShell stuff here if no commands were handled
}
- private void startTextLogging(String... groups) {
- ShellProtoLogImpl.getSingleInstance().startTextLogging(mContext, groups);
+ @Override
+ public void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd) {
+ PrintWriter pw = new PrintWriter(new ParcelFileDescriptor.AutoCloseOutputStream(outFd));
+ handleLoggingCommand(args, pw);
+ pw.flush();
+ pw.close();
}
- private void stopTextLogging(String... groups) {
- ShellProtoLogImpl.getSingleInstance().stopTextLogging(groups);
+ private boolean handleLoggingCommand(String[] args, PrintWriter pw) {
+ ShellProtoLogImpl protoLogImpl = ShellProtoLogImpl.getSingleInstance();
+ for (int i = 0; i < args.length; i++) {
+ switch (args[i]) {
+ case "enable-text": {
+ String[] groups = Arrays.copyOfRange(args, i + 1, args.length);
+ int result = protoLogImpl.startTextLogging(mContext, groups, pw);
+ if (result == 0) {
+ pw.println("Starting logging on groups: " + Arrays.toString(groups));
+ }
+ return true;
+ }
+ case "disable-text": {
+ String[] groups = Arrays.copyOfRange(args, i + 1, args.length);
+ int result = protoLogImpl.stopTextLogging(groups, pw);
+ if (result == 0) {
+ pw.println("Stopping logging on groups: " + Arrays.toString(groups));
+ }
+ return true;
+ }
+ }
+ }
+ return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 3faa8a7..7c129ac 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -17,6 +17,7 @@
package com.android.systemui.wmshell;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.view.IWindowManager;
@@ -76,8 +77,9 @@
@SysUISingleton
@Provides
- static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger) {
- return new PipUiEventLogger(uiEventLogger);
+ static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger,
+ PackageManager packageManager) {
+ return new PipUiEventLogger(uiEventLogger, packageManager);
}
@SysUISingleton
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
index 9be2d12..dffad6c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
@@ -41,8 +41,6 @@
import android.testing.ViewUtils;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceView;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
@@ -67,7 +65,7 @@
private ComponentName mComponentName;
private Intent mServiceIntent;
private TestableLooper mTestableLooper;
- private ViewGroup mParent;
+ private KeyguardSecurityContainer mKeyguardSecurityContainer;
@Mock
private Handler mHandler;
@@ -84,8 +82,8 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
- mParent = spy(new FrameLayout(mContext));
- ViewUtils.attachView(mParent);
+ mKeyguardSecurityContainer = spy(new KeyguardSecurityContainer(mContext));
+ ViewUtils.attachView(mKeyguardSecurityContainer);
mTestableLooper = TestableLooper.get(this);
mComponentName = new ComponentName(mContext, "FakeKeyguardClient.class");
@@ -96,13 +94,14 @@
when(mKeyguardClient.queryLocalInterface(anyString())).thenReturn(mKeyguardClient);
when(mKeyguardClient.asBinder()).thenReturn(mKeyguardClient);
- mTestController = new AdminSecondaryLockScreenController(
- mContext, mParent, mUpdateMonitor, mKeyguardCallback, mHandler);
+ mTestController = new AdminSecondaryLockScreenController.Factory(
+ mContext, mKeyguardSecurityContainer, mUpdateMonitor, mHandler)
+ .create(mKeyguardCallback);
}
@After
public void tearDown() {
- ViewUtils.detachView(mParent);
+ ViewUtils.detachView(mKeyguardSecurityContainer);
}
@Test
@@ -146,7 +145,7 @@
SurfaceView v = verifySurfaceReady();
mTestController.hide();
- verify(mParent).removeView(v);
+ verify(mKeyguardSecurityContainer).removeView(v);
assertThat(mContext.isBound(mComponentName)).isFalse();
}
@@ -154,7 +153,7 @@
public void testHide_notShown() throws Exception {
mTestController.hide();
// Nothing should happen if trying to hide when the view isn't attached yet.
- verify(mParent, never()).removeView(any(SurfaceView.class));
+ verify(mKeyguardSecurityContainer, never()).removeView(any(SurfaceView.class));
}
@Test
@@ -182,7 +181,7 @@
private SurfaceView verifySurfaceReady() throws Exception {
mTestableLooper.processAllMessages();
ArgumentCaptor<SurfaceView> captor = ArgumentCaptor.forClass(SurfaceView.class);
- verify(mParent).addView(captor.capture());
+ verify(mKeyguardSecurityContainer).addView(captor.capture());
mTestableLooper.processAllMessages();
verify(mKeyguardClient).onCreateKeyguardSurface(any(), any(IKeyguardCallback.class));
@@ -190,7 +189,7 @@
}
private void verifyViewDismissed(SurfaceView v) throws Exception {
- verify(mParent).removeView(v);
+ verify(mKeyguardSecurityContainer).removeView(v);
verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID, true);
assertThat(mContext.isBound(mComponentName)).isFalse();
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
new file mode 100644
index 0000000..c2ade81
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.KeyEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase {
+
+ @Mock
+ private KeyguardAbsKeyInputView mAbsKeyInputView;
+ @Mock
+ private PasswordTextView mPasswordEntry;
+ @Mock
+ private KeyguardMessageArea mKeyguardMessageArea;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private SecurityMode mSecurityMode;
+ @Mock
+ private LockPatternUtils mLockPatternUtils;
+ @Mock
+ private KeyguardSecurityCallback mKeyguardSecurityCallback;
+ @Mock
+ private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory;
+ @Mock
+ private KeyguardMessageAreaController mKeyguardMessageAreaController;
+ @Mock
+ private LatencyTracker mLatencyTracker;
+
+ private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class)))
+ .thenReturn(mKeyguardMessageAreaController);
+ when(mAbsKeyInputView.getPasswordTextViewId()).thenReturn(1);
+ when(mAbsKeyInputView.findViewById(1)).thenReturn(mPasswordEntry);
+ when(mAbsKeyInputView.isAttachedToWindow()).thenReturn(true);
+ when(mAbsKeyInputView.findViewById(R.id.keyguard_message_area))
+ .thenReturn(mKeyguardMessageArea);
+ mKeyguardAbsKeyInputViewController = new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
+ mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
+ mKeyguardMessageAreaControllerFactory, mLatencyTracker) {
+ @Override
+ void resetState() {
+ }
+
+ @Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ }
+ };
+ mKeyguardAbsKeyInputViewController.init();
+ reset(mKeyguardMessageAreaController); // Clear out implicit call to init.
+ }
+
+ @Test
+ public void onKeyDown_clearsSecurityMessage() {
+ ArgumentCaptor<KeyDownListener> onKeyDownListenerArgumentCaptor =
+ ArgumentCaptor.forClass(KeyDownListener.class);
+ verify(mAbsKeyInputView).setKeyDownListener(onKeyDownListenerArgumentCaptor.capture());
+ onKeyDownListenerArgumentCaptor.getValue().onKeyDown(
+ KeyEvent.KEYCODE_0, mock(KeyEvent.class));
+ verify(mKeyguardSecurityCallback).userActivity();
+ verify(mKeyguardMessageAreaController).setMessage(eq(""));
+ }
+
+ @Test
+ public void onKeyDown_noSecurityMessageInteraction() {
+ ArgumentCaptor<KeyDownListener> onKeyDownListenerArgumentCaptor =
+ ArgumentCaptor.forClass(KeyDownListener.class);
+ verify(mAbsKeyInputView).setKeyDownListener(onKeyDownListenerArgumentCaptor.capture());
+ onKeyDownListenerArgumentCaptor.getValue().onKeyDown(
+ KeyEvent.KEYCODE_UNKNOWN, mock(KeyEvent.class));
+ verifyZeroInteractions(mKeyguardSecurityCallback);
+ verifyZeroInteractions(mKeyguardMessageAreaController);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 5999e2c..e793079 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -41,11 +41,9 @@
import android.widget.TextClock;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.util.InjectionInflationController;
import org.junit.Before;
import org.junit.Test;
@@ -78,12 +76,7 @@
when(mMockKeyguardSliceView.findViewById(R.id.keyguard_status_area))
.thenReturn(mMockKeyguardSliceView);
- InjectionInflationController inflationController = new InjectionInflationController(
- SystemUIFactory.getInstance()
- .getSysUIComponent()
- .createViewInstanceCreatorFactory());
- LayoutInflater layoutInflater = inflationController
- .injectable(LayoutInflater.from(getContext()));
+ LayoutInflater layoutInflater = LayoutInflater.from(getContext());
layoutInflater.setPrivateFactory(new LayoutInflater.Factory2() {
@Override
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
new file mode 100644
index 0000000..a7197cc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
@@ -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.keyguard;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class KeyguardMessageAreaControllerTest extends SysuiTestCase {
+ @Mock
+ private ConfigurationController mConfigurationController;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private KeyguardMessageArea mKeyguardMessageArea;
+
+ private KeyguardMessageAreaController mMessageAreaController;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mMessageAreaController = new KeyguardMessageAreaController.Factory(
+ mKeyguardUpdateMonitor, mConfigurationController).create(mKeyguardMessageArea);
+ }
+
+ @Test
+ public void onAttachedToWindow_registersConfigurationCallback() {
+ ArgumentCaptor<ConfigurationListener> configurationListenerArgumentCaptor =
+ ArgumentCaptor.forClass(ConfigurationListener.class);
+
+ mMessageAreaController.onViewAttached();
+ verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture());
+
+ mMessageAreaController.onViewDetached();
+ verify(mConfigurationController).removeCallback(
+ eq(configurationListenerArgumentCaptor.getValue()));
+ }
+
+ @Test
+ public void onAttachedToWindow_registersKeyguardUpdateMontiorCallback() {
+ ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+
+ mMessageAreaController.onViewAttached();
+ verify(mKeyguardUpdateMonitor).registerCallback(
+ keyguardUpdateMonitorCallbackArgumentCaptor.capture());
+
+ mMessageAreaController.onViewDetached();
+ verify(mKeyguardUpdateMonitor).removeCallback(
+ eq(keyguardUpdateMonitorCallbackArgumentCaptor.getValue()));
+ }
+
+ @Test
+ public void testClearsTextField() {
+ mMessageAreaController.setMessage("");
+ verify(mKeyguardMessageArea).setMessage("");
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
index fc7b9a4..31fb25a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 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.
@@ -11,65 +11,60 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
package com.android.keyguard;
-import static junit.framework.Assert.assertEquals;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
+import static com.google.common.truth.Truth.assertThat;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class KeyguardMessageAreaTest extends SysuiTestCase {
- @Mock
- private ConfigurationController mConfigurationController;
- @Mock
- private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private KeyguardMessageArea mMessageArea;
+ private KeyguardMessageArea mKeyguardMessageArea;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mMessageArea = new KeyguardMessageArea(mContext, null, mKeyguardUpdateMonitor,
- mConfigurationController);
- waitForIdleSync();
+ mKeyguardMessageArea = new KeyguardMessageArea(mContext, null);
+ mKeyguardMessageArea.setBouncerVisible(true);
}
@Test
- public void onAttachedToWindow_registersConfigurationCallback() {
- mMessageArea.onAttachedToWindow();
- verify(mConfigurationController).addCallback(eq(mMessageArea));
-
- mMessageArea.onDetachedFromWindow();
- verify(mConfigurationController).removeCallback(eq(mMessageArea));
+ public void testShowsTextField() {
+ mKeyguardMessageArea.setVisibility(View.INVISIBLE);
+ mKeyguardMessageArea.setMessage("oobleck");
+ assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mKeyguardMessageArea.getText()).isEqualTo("oobleck");
}
@Test
- public void clearFollowedByMessage_keepsMessage() {
- mMessageArea.setMessage("");
- mMessageArea.setMessage("test");
-
- CharSequence[] messageText = new CharSequence[1];
- messageText[0] = mMessageArea.getText();
-
- assertEquals("test", messageText[0]);
+ public void testHiddenWhenBouncerHidden() {
+ mKeyguardMessageArea.setBouncerVisible(false);
+ mKeyguardMessageArea.setVisibility(View.INVISIBLE);
+ mKeyguardMessageArea.setMessage("oobleck");
+ assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.INVISIBLE);
+ assertThat(mKeyguardMessageArea.getText()).isEqualTo("oobleck");
}
+ @Test
+ public void testClearsTextField() {
+ mKeyguardMessageArea.setVisibility(View.VISIBLE);
+ mKeyguardMessageArea.setMessage("");
+ assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.INVISIBLE);
+ assertThat(mKeyguardMessageArea.getText()).isEqualTo("");
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
new file mode 100644
index 0000000..c69ec1a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -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 com.android.keyguard
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.util.LatencyTracker
+import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockPatternView
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class KeyguardPatternViewControllerTest : SysuiTestCase() {
+ @Mock
+ private lateinit var mKeyguardPatternView: KeyguardPatternView
+ @Mock
+ private lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock
+ private lateinit var mSecurityMode: KeyguardSecurityModel.SecurityMode
+ @Mock
+ private lateinit var mLockPatternUtils: LockPatternUtils
+ @Mock
+ private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback
+ @Mock
+ private lateinit var mLatencyTracker: LatencyTracker
+ @Mock
+ private lateinit
+ var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory
+ @Mock
+ private lateinit var mKeyguardMessageArea: KeyguardMessageArea
+ @Mock
+ private lateinit var mKeyguardMessageAreaController: KeyguardMessageAreaController
+ @Mock
+ private lateinit var mLockPatternView: LockPatternView
+
+ private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ `when`(mKeyguardPatternView.isAttachedToWindow).thenReturn(true)
+ `when`(mKeyguardPatternView.findViewById<KeyguardMessageArea>(R.id.keyguard_message_area))
+ .thenReturn(mKeyguardMessageArea)
+ `when`(mKeyguardPatternView.findViewById<LockPatternView>(R.id.lockPatternView))
+ .thenReturn(mLockPatternView)
+ `when`(mKeyguardMessageAreaControllerFactory.create(mKeyguardMessageArea))
+ .thenReturn(mKeyguardMessageAreaController)
+ mKeyguardPatternViewController = KeyguardPatternViewController(mKeyguardPatternView,
+ mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
+ mLatencyTracker, mKeyguardMessageAreaControllerFactory)
+ }
+
+ @Test
+ fun onPause_clearsTextField() {
+ mKeyguardPatternViewController.init()
+ mKeyguardPatternViewController.onPause()
+ verify(mKeyguardMessageAreaController).setMessage("")
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt
deleted file mode 100644
index b4363cf..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.keyguard
-
-import androidx.test.filters.SmallTest
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.LayoutInflater
-
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.google.common.truth.Truth.assertThat
-
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.mock
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
-class KeyguardPatternViewTest : SysuiTestCase() {
-
- private lateinit var mKeyguardPatternView: KeyguardPatternView
- private lateinit var mSecurityMessage: KeyguardMessageArea
-
- @Before
- fun setup() {
- val inflater = LayoutInflater.from(context)
- mDependency.injectMockDependency(KeyguardUpdateMonitor::class.java)
- mKeyguardPatternView = inflater.inflate(R.layout.keyguard_pattern_view, null)
- as KeyguardPatternView
- mSecurityMessage = KeyguardMessageArea(mContext, null,
- mock(KeyguardUpdateMonitor::class.java), mock(ConfigurationController::class.java))
- mKeyguardPatternView.mSecurityMessageDisplay = mSecurityMessage
- }
-
- @Test
- fun onPause_clearsTextField() {
- mSecurityMessage.setMessage("an old message")
- mKeyguardPatternView.onPause()
- assertThat(mSecurityMessage.text).isEqualTo("")
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
new file mode 100644
index 0000000..4944284
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.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.keyguard;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
+
+ @Mock
+ private KeyguardPinBasedInputView mPinBasedInputView;
+ @Mock
+ private PasswordTextView mPasswordEntry;
+ @Mock
+ private KeyguardMessageArea mKeyguardMessageArea;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private SecurityMode mSecurityMode;
+ @Mock
+ private LockPatternUtils mLockPatternUtils;
+ @Mock
+ private KeyguardSecurityCallback mKeyguardSecurityCallback;
+ @Mock
+ private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory;
+ @Mock
+ private KeyguardMessageAreaController mKeyguardMessageAreaController;
+ @Mock
+ private LatencyTracker mLatencyTracker;
+ @Mock
+ private LiftToActivateListener mLiftToactivateListener;
+ @Mock
+ private View mDeleteButton;
+ @Mock
+ private View mOkButton;
+
+ private KeyguardPinBasedInputViewController mKeyguardPinViewController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class)))
+ .thenReturn(mKeyguardMessageAreaController);
+ when(mPinBasedInputView.getPasswordTextViewId()).thenReturn(1);
+ when(mPinBasedInputView.findViewById(1)).thenReturn(mPasswordEntry);
+ when(mPinBasedInputView.isAttachedToWindow()).thenReturn(true);
+ when(mPinBasedInputView.findViewById(R.id.keyguard_message_area))
+ .thenReturn(mKeyguardMessageArea);
+ when(mPinBasedInputView.findViewById(R.id.delete_button))
+ .thenReturn(mDeleteButton);
+ when(mPinBasedInputView.findViewById(R.id.key_enter))
+ .thenReturn(mOkButton);
+ mKeyguardPinViewController = new KeyguardPinBasedInputViewController(mPinBasedInputView,
+ mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
+ mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener) {
+ @Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ }
+ };
+ mKeyguardPinViewController.init();
+ }
+
+ @Test
+ public void onResume_requestsFocus() {
+ mKeyguardPinViewController.onResume(KeyguardSecurityView.SCREEN_ON);
+ verify(mPasswordEntry).requestFocus();
+ }
+}
+
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java
deleted file mode 100644
index 6666a92..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.keyguard;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-public class KeyguardPinBasedInputViewTest extends SysuiTestCase {
-
- @Mock
- private PasswordTextView mPasswordEntry;
- @Mock
- private SecurityMessageDisplay mSecurityMessageDisplay;
- @InjectMocks
- private KeyguardPinBasedInputView mKeyguardPinView;
-
- @Before
- public void setup() {
- LayoutInflater inflater = LayoutInflater.from(getContext());
- mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
- mKeyguardPinView =
- (KeyguardPinBasedInputView) inflater.inflate(R.layout.keyguard_pin_view, null);
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void onResume_requestsFocus() {
- mKeyguardPinView.onResume(KeyguardSecurityView.SCREEN_ON);
- verify(mPasswordEntry).requestFocus();
- }
-
- @Test
- public void onKeyDown_clearsSecurityMessage() {
- mKeyguardPinView.onKeyDown(KeyEvent.KEYCODE_0, mock(KeyEvent.class));
- verify(mSecurityMessageDisplay).setMessage(eq(""));
- }
-
- @Test
- public void onKeyDown_noSecurityMessageInteraction() {
- mKeyguardPinView.onKeyDown(KeyEvent.KEYCODE_UNKNOWN, mock(KeyEvent.class));
- verifyZeroInteractions(mSecurityMessageDisplay);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
index 559284a..ae159c7 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
@@ -31,9 +31,7 @@
import com.android.keyguard.KeyguardDisplayManager.KeyguardPresentation;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.InjectionInflationController;
import org.junit.After;
import org.junit.Before;
@@ -65,7 +63,6 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
when(mMockKeyguardClockSwitch.getContext()).thenReturn(mContext);
when(mMockKeyguardSliceView.getContext()).thenReturn(mContext);
when(mMockKeyguardStatusView.getContext()).thenReturn(mContext);
@@ -77,11 +74,7 @@
allowTestableLooperAsMainThread();
- InjectionInflationController inflationController = new InjectionInflationController(
- SystemUIFactory.getInstance()
- .getSysUIComponent()
- .createViewInstanceCreatorFactory());
- mLayoutInflater = inflationController.injectable(LayoutInflater.from(mContext));
+ mLayoutInflater = LayoutInflater.from(mContext);
mLayoutInflater.setPrivateFactory(new LayoutInflater.Factory2() {
@Override
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
new file mode 100644
index 0000000..cdb91ec
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.keyguard;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.WindowInsetsController;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper()
+public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
+
+ @Rule
+ public MockitoRule mRule = MockitoJUnit.rule();
+
+ @Mock
+ private KeyguardSecurityContainer mView;
+ @Mock
+ private AdminSecondaryLockScreenController.Factory mAdminSecondaryLockScreenControllerFactory;
+ @Mock
+ private AdminSecondaryLockScreenController mAdminSecondaryLockScreenController;
+ @Mock
+ private LockPatternUtils mLockPatternUtils;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private KeyguardSecurityModel mKeyguardSecurityModel;
+ @Mock
+ private MetricsLogger mMetricsLogger;
+ @Mock
+ private UiEventLogger mUiEventLogger;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+ @Mock
+ private KeyguardInputViewController mInputViewController;
+ @Mock
+ private KeyguardSecurityContainer.SecurityCallback mSecurityCallback;
+ @Mock
+ private WindowInsetsController mWindowInsetsController;
+ @Mock
+ private KeyguardSecurityViewFlipper mSecurityViewFlipper;
+ @Mock
+ private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController;
+
+ private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
+
+ @Before
+ public void setup() {
+ when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class)))
+ .thenReturn(mAdminSecondaryLockScreenController);
+ when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
+
+ mKeyguardSecurityContainerController = new KeyguardSecurityContainerController(
+ mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
+ mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
+ mKeyguardStateController, mKeyguardSecurityViewFlipperController);
+
+ mKeyguardSecurityContainerController.setSecurityCallback(mSecurityCallback);
+ }
+
+ @Test
+ public void showSecurityScreen_canInflateAllModes() {
+ SecurityMode[] modes = SecurityMode.values();
+ for (SecurityMode mode : modes) {
+ when(mInputViewController.getSecurityMode()).thenReturn(mode);
+ mKeyguardSecurityContainerController.showSecurityScreen(mode);
+ if (mode == SecurityMode.Invalid) {
+ verify(mKeyguardSecurityViewFlipperController, never()).getSecurityView(
+ any(SecurityMode.class), any(KeyguardSecurityCallback.class));
+ } else {
+ verify(mKeyguardSecurityViewFlipperController).getSecurityView(
+ eq(mode), any(KeyguardSecurityCallback.class));
+ }
+ }
+ }
+
+ @Test
+ public void startDisappearAnimation_animatesKeyboard() {
+ when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
+ SecurityMode.Password);
+ when(mInputViewController.getSecurityMode()).thenReturn(
+ SecurityMode.Password);
+ when(mKeyguardSecurityViewFlipperController.getSecurityView(
+ eq(SecurityMode.Password), any(KeyguardSecurityCallback.class)))
+ .thenReturn(mInputViewController);
+ mKeyguardSecurityContainerController.showPrimarySecurityScreen(false /* turningOff */);
+
+ mKeyguardSecurityContainerController.startDisappearAnimation(null);
+ verify(mInputViewController).startDisappearAnimation(eq(null));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index a867825..854be1f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -19,23 +19,19 @@
import static android.view.WindowInsets.Type.ime;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.Context;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.view.LayoutInflater;
import android.view.WindowInsetsController;
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Rule;
@@ -50,68 +46,26 @@
@TestableLooper.RunWithLooper()
public class KeyguardSecurityContainerTest extends SysuiTestCase {
- @Mock
- private KeyguardSecurityModel mKeyguardSecurityModel;
- @Mock
- private KeyguardStateController mKeyguardStateController;
- @Mock
- private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock
- private KeyguardSecurityContainer.SecurityCallback mSecurityCallback;
- @Mock
- private KeyguardSecurityView mSecurityView;
+ @Rule
+ public MockitoRule mRule = MockitoJUnit.rule();
+
@Mock
private WindowInsetsController mWindowInsetsController;
@Mock
private KeyguardSecurityViewFlipper mSecurityViewFlipper;
- @Rule
- public MockitoRule mRule = MockitoJUnit.rule();
+
private KeyguardSecurityContainer mKeyguardSecurityContainer;
@Before
public void setup() {
- mDependency.injectTestDependency(KeyguardStateController.class, mKeyguardStateController);
- mDependency.injectTestDependency(KeyguardSecurityModel.class, mKeyguardSecurityModel);
- mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor);
- mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext()) {
- @Override
- protected KeyguardSecurityView getSecurityView(
- KeyguardSecurityModel.SecurityMode securityMode) {
- return mSecurityView;
- }
- };
- mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper;
when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
- mKeyguardSecurityContainer.setSecurityCallback(mSecurityCallback);
- }
-
- @Test
- public void showSecurityScreen_canInflateAllModes() {
- Context context = getContext();
-
- for (int theme : new int[] {R.style.Theme_SystemUI, R.style.Theme_SystemUI_Light}) {
- context.setTheme(theme);
- final LayoutInflater inflater = LayoutInflater.from(context);
- KeyguardSecurityModel.SecurityMode[] modes =
- KeyguardSecurityModel.SecurityMode.values();
- for (KeyguardSecurityModel.SecurityMode mode : modes) {
- final int resId = mKeyguardSecurityContainer.getLayoutIdFor(mode);
- if (resId == 0) {
- continue;
- }
- inflater.inflate(resId, null /* root */, false /* attach */);
- }
- }
+ mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext());
+ mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper;
}
@Test
public void startDisappearAnimation_animatesKeyboard() {
- when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
- KeyguardSecurityModel.SecurityMode.Password);
- mKeyguardSecurityContainer.showPrimarySecurityScreen(false /* turningOff */);
-
- mKeyguardSecurityContainer.startDisappearAnimation(null);
- verify(mSecurityView).startDisappearAnimation(eq(null));
+ mKeyguardSecurityContainer.startDisappearAnimation(SecurityMode.Password);
verify(mWindowInsetsController).controlWindowInsetsAnimation(eq(ime()), anyLong(), any(),
any(), any());
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
new file mode 100644
index 0000000..3b7f4b8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.keyguard;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.view.WindowInsetsController;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper()
+public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase {
+
+ @Rule
+ public MockitoRule mRule = MockitoJUnit.rule();
+
+ @Mock
+ private KeyguardSecurityViewFlipper mView;
+ @Mock
+ private LayoutInflater mLayoutInflater;
+ @Mock
+ private KeyguardInputViewController.Factory mKeyguardSecurityViewControllerFactory;
+ @Mock
+ private KeyguardInputViewController mKeyguardInputViewController;
+ @Mock
+ private KeyguardInputView mInputView;
+ @Mock
+ private WindowInsetsController mWindowInsetsController;
+ @Mock
+ private KeyguardSecurityCallback mKeyguardSecurityCallback;
+
+ private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController;
+
+ @Before
+ public void setup() {
+ when(mKeyguardSecurityViewControllerFactory.create(
+ any(KeyguardInputView.class), any(SecurityMode.class),
+ any(KeyguardSecurityCallback.class)))
+ .thenReturn(mKeyguardInputViewController);
+ when(mView.getWindowInsetsController()).thenReturn(mWindowInsetsController);
+
+ mKeyguardSecurityViewFlipperController = new KeyguardSecurityViewFlipperController(mView,
+ mLayoutInflater, mKeyguardSecurityViewControllerFactory);
+ }
+
+ @Test
+ public void showSecurityScreen_canInflateAllModes() {
+ SecurityMode[] modes = SecurityMode.values();
+ // Always return an invalid controller so that we're always making a new one.
+ when(mKeyguardInputViewController.getSecurityMode()).thenReturn(SecurityMode.Invalid);
+ for (SecurityMode mode : modes) {
+ reset(mLayoutInflater);
+ when(mLayoutInflater.inflate(anyInt(), eq(mView), eq(false)))
+ .thenReturn(mInputView);
+ mKeyguardSecurityViewFlipperController.getSecurityView(mode, mKeyguardSecurityCallback);
+ if (mode == SecurityMode.Invalid || mode == SecurityMode.None) {
+ verify(mLayoutInflater, never()).inflate(
+ anyInt(), any(ViewGroup.class), anyBoolean());
+ } else {
+ verify(mLayoutInflater).inflate(anyInt(), eq(mView), eq(false));
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
index 0431704..79ec4f2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
@@ -24,9 +24,7 @@
import android.view.LayoutInflater;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.InjectionInflationController;
import org.junit.Before;
import org.junit.Test;
@@ -50,13 +48,7 @@
@Before
public void setUp() {
allowTestableLooperAsMainThread();
- mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
- InjectionInflationController inflationController = new InjectionInflationController(
- SystemUIFactory.getInstance()
- .getSysUIComponent()
- .createViewInstanceCreatorFactory());
- LayoutInflater layoutInflater = inflationController
- .injectable(LayoutInflater.from(getContext()));
+ LayoutInflater layoutInflater = LayoutInflater.from(getContext());
mKeyguardStatusView =
(KeyguardStatusView) layoutInflater.inflate(R.layout.keyguard_status_view, null);
org.mockito.MockitoAnnotations.initMocks(this);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 0fe3817..86b1e61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -41,7 +41,6 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import org.junit.After;
import org.junit.Before;
@@ -110,7 +109,6 @@
// KeyguardUpdateMonitor to be created (injected).
// TODO(b/1531701009) Clean up NotificationContentView creation to prevent this
mDependency.injectMockDependency(SmartReplyController.class);
- mDependency.injectMockDependency(NotificationBlockingHelperManager.class);
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
index 9079338..1e969c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
@@ -23,13 +23,13 @@
import static org.mockito.Mockito.verify;
import android.content.Context;
-import android.os.RemoteCallback;
import android.os.RemoteException;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Display;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
@@ -41,7 +41,6 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -66,8 +65,7 @@
@Mock
private ModeSwitchesController mModeSwitchesController;
@Mock
- private RemoteCallback mRemoteCallback;
- private ArgumentCaptor<Runnable> mRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ private IRemoteMagnificationAnimationCallback mAnimationCallback;
private IWindowMagnificationConnection mIWindowMagnificationConnection;
private WindowMagnification mWindowMagnification;
@@ -92,22 +90,21 @@
@Test
public void enableWindowMagnification_passThrough() throws RemoteException {
mIWindowMagnificationConnection.enableWindowMagnification(TEST_DISPLAY, 3.0f, Float.NaN,
- Float.NaN, mRemoteCallback);
+ Float.NaN, mAnimationCallback);
waitForIdleSync();
verify(mWindowMagnificationAnimationController).enableWindowMagnification(eq(3.0f),
- eq(Float.NaN), eq(Float.NaN), mRunnableCaptor.capture());
- verifyRunnableWrapsRemoteCallback(mRunnableCaptor.getValue());
+ eq(Float.NaN), eq(Float.NaN), eq(mAnimationCallback));
}
@Test
public void disableWindowMagnification_deleteWindowMagnification() throws RemoteException {
- mIWindowMagnificationConnection.disableWindowMagnification(TEST_DISPLAY, mRemoteCallback);
+ mIWindowMagnificationConnection.disableWindowMagnification(TEST_DISPLAY,
+ mAnimationCallback);
waitForIdleSync();
verify(mWindowMagnificationAnimationController).deleteWindowMagnification(
- mRunnableCaptor.capture());
- verifyRunnableWrapsRemoteCallback(mRunnableCaptor.getValue());
+ mAnimationCallback);
}
@Test
@@ -143,10 +140,5 @@
verify(mModeSwitchesController).removeButton(TEST_DISPLAY);
}
-
- private void verifyRunnableWrapsRemoteCallback(Runnable runnable) {
- runnable.run();
- verify(mRemoteCallback).sendResult(null);
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index d74c62b..33b1d94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.never;
@@ -28,9 +29,11 @@
import android.app.Instrumentation;
import android.content.Context;
import android.os.Handler;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.testing.AndroidTestingRunner;
import android.view.SurfaceControl;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.animation.AccelerateInterpolator;
import androidx.test.InstrumentationRegistry;
@@ -75,9 +78,9 @@
@Mock
WindowMagnifierCallback mWindowMagnifierCallback;
@Mock
- Runnable mAnimationEndCallback;
+ IRemoteMagnificationAnimationCallback mAnimationCallback;
@Mock
- Runnable mAnimationEndCallback2;
+ IRemoteMagnificationAnimationCallback mAnimationCallback2;
private SpyWindowMagnificationController mController;
private WindowMagnificationController mSpyController;
private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
@@ -105,8 +108,9 @@
}
@Test
- public void enableWindowMagnification_disabled_expectedValuesAndInvokeCallback() {
- enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback);
+ public void enableWindowMagnification_disabled_expectedValuesAndInvokeCallback()
+ throws RemoteException {
+ enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
verify(mSpyController, atLeast(2)).enableWindowMagnification(
mScaleCaptor.capture(),
@@ -115,28 +119,30 @@
verifyStartValue(mCenterXCaptor, DEFAULT_CENTER_X);
verifyStartValue(mCenterYCaptor, DEFAULT_CENTER_Y);
verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
- verify(mAnimationEndCallback).run();
+ verify(mAnimationCallback).onResult(true);
}
@Test
- public void enableWindowMagnificationWithScaleOne_disabled_NoAnimationAndInvokeCallback() {
+ public void enableWindowMagnificationWithScaleOne_disabled_NoAnimationAndInvokeCallback()
+ throws RemoteException {
mInstrumentation.runOnMainSync(
() -> {
mWindowMagnificationAnimationController.enableWindowMagnification(1,
- DEFAULT_CENTER_X, DEFAULT_CENTER_Y, mAnimationEndCallback);
+ DEFAULT_CENTER_X, DEFAULT_CENTER_Y, mAnimationCallback);
});
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController).enableWindowMagnification(1, DEFAULT_CENTER_X,
DEFAULT_CENTER_Y);
- verify(mAnimationEndCallback).run();
+ verify(mAnimationCallback).onResult(true);
}
@Test
- public void enableWindowMagnification_enabling_expectedValuesAndInvokeCallback() {
+ public void enableWindowMagnification_enabling_expectedValuesAndInvokeCallback()
+ throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
- mAnimationEndCallback);
+ mAnimationCallback);
final float targetScale = DEFAULT_SCALE + 1.0f;
final float targetCenterX = DEFAULT_CENTER_X + 100;
final float targetCenterY = DEFAULT_CENTER_Y + 100;
@@ -144,7 +150,7 @@
mInstrumentation.runOnMainSync(() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
- targetCenterX, targetCenterY, mAnimationEndCallback2);
+ targetCenterX, targetCenterY, mAnimationCallback2);
mCurrentScale.set(mController.getScale());
mCurrentCenterX.set(mController.getCenterX());
mCurrentCenterY.set(mController.getCenterY());
@@ -158,33 +164,35 @@
verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
- verify(mAnimationEndCallback, never()).run();
- verify(mAnimationEndCallback2).run();
+ verify(mAnimationCallback).onResult(false);
+ verify(mAnimationCallback2).onResult(true);
}
@Test
- public void enableWindowMagnificationWithSameSpec_enabling_NoAnimationAndInvokeCallback() {
+ public void enableWindowMagnificationWithSameSpec_enabling_NoAnimationAndInvokeCallback()
+ throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
- mAnimationEndCallback);
+ mAnimationCallback);
mInstrumentation.runOnMainSync(() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN,
- Float.NaN, Float.NaN, mAnimationEndCallback2);
+ Float.NaN, Float.NaN, mAnimationCallback2);
});
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
anyFloat());
- verify(mAnimationEndCallback, never()).run();
- verify(mAnimationEndCallback2).run();
+ verify(mAnimationCallback).onResult(false);
+ verify(mAnimationCallback2).onResult(true);
}
@Test
- public void enableWindowMagnification_disabling_expectedValuesAndInvokeCallback() {
+ public void enableWindowMagnification_disabling_expectedValuesAndInvokeCallback()
+ throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
- mAnimationEndCallback);
+ mAnimationCallback);
final float targetScale = DEFAULT_SCALE + 1.0f;
final float targetCenterX = DEFAULT_CENTER_X + 100;
final float targetCenterY = DEFAULT_CENTER_Y + 100;
@@ -193,13 +201,14 @@
() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
- targetCenterX, targetCenterY, mAnimationEndCallback2);
+ targetCenterX, targetCenterY, mAnimationCallback2);
mCurrentScale.set(mController.getScale());
mCurrentCenterX.set(mController.getCenterX());
mCurrentCenterY.set(mController.getCenterY());
});
// Current spec shouldn't match given spec.
- verify(mAnimationEndCallback2, never()).run();
+ verify(mAnimationCallback2, never()).onResult(anyBoolean());
+ verify(mAnimationCallback).onResult(false);
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController, atLeast(2)).enableWindowMagnification(
@@ -213,34 +222,35 @@
assertTrue(mCenterYCaptor.getAllValues().get(0) > mCurrentCenterY.get());
assertEquals(targetCenterY, mCenterYCaptor.getValue(), 0f);
verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
- verify(mAnimationEndCallback, never()).run();
- verify(mAnimationEndCallback2).run();
+ verify(mAnimationCallback2).onResult(true);
}
@Test
- public void enableWindowMagnificationWithSameSpec_disabling_NoAnimationAndInvokeCallback() {
+ public void enableWindowMagnificationWithSameSpec_disabling_NoAnimationAndInvokeCallback()
+ throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
- mAnimationEndCallback);
+ mAnimationCallback);
mInstrumentation.runOnMainSync(() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN,
- Float.NaN, Float.NaN, mAnimationEndCallback2);
+ Float.NaN, Float.NaN, mAnimationCallback2);
});
SystemClock.sleep(mWaitingAnimationPeriod);
verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
anyFloat());
verify(mSpyController, never()).deleteWindowMagnification();
- verify(mAnimationEndCallback, never()).run();
- verify(mAnimationEndCallback2).run();
+ verify(mAnimationCallback).onResult(false);
+ verify(mAnimationCallback2).onResult(true);
}
@Test
- public void enableWindowMagnification_enabled_expectedValuesAndInvokeCallback() {
+ public void enableWindowMagnification_enabled_expectedValuesAndInvokeCallback()
+ throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
- mAnimationEndCallback);
+ mAnimationCallback);
final float targetScale = DEFAULT_SCALE + 1.0f;
final float targetCenterX = DEFAULT_CENTER_X + 100;
final float targetCenterY = DEFAULT_CENTER_Y + 100;
@@ -248,7 +258,7 @@
mInstrumentation.runOnMainSync(() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
- targetCenterX, targetCenterY, mAnimationEndCallback2);
+ targetCenterX, targetCenterY, mAnimationCallback2);
mCurrentScale.set(mController.getScale());
mCurrentCenterX.set(mController.getCenterX());
mCurrentCenterY.set(mController.getCenterY());
@@ -262,19 +272,20 @@
verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
- verify(mAnimationEndCallback, never()).run();
- verify(mAnimationEndCallback2).run();
+ verify(mAnimationCallback).onResult(false);
+ verify(mAnimationCallback2).onResult(true);
}
@Test
- public void enableWindowMagnificationWithSameScale_enabled_doNothingButInvokeCallback() {
+ public void enableWindowMagnificationWithSameScale_enabled_doNothingButInvokeCallback()
+ throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
- enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback);
+ enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
anyFloat());
- verify(mAnimationEndCallback).run();
+ verify(mAnimationCallback).onResult(true);
}
@Test
@@ -289,10 +300,11 @@
}
@Test
- public void deleteWindowMagnification_enabled_expectedValuesAndInvokeCallback() {
+ public void deleteWindowMagnification_enabled_expectedValuesAndInvokeCallback()
+ throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
- deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback);
+ deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture());
@@ -301,27 +313,29 @@
verifyStartValue(mCenterXCaptor, Float.NaN);
verifyStartValue(mCenterYCaptor, Float.NaN);
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
- verify(mAnimationEndCallback).run();
+ verify(mAnimationCallback).onResult(true);
}
@Test
- public void deleteWindowMagnification_disabled_doNothingAndInvokeCallback() {
- deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback);
+ public void deleteWindowMagnification_disabled_doNothingAndInvokeCallback()
+ throws RemoteException {
+ deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
Mockito.verifyNoMoreInteractions(mSpyController);
- verify(mAnimationEndCallback).run();
+ verify(mAnimationCallback).onResult(true);
}
@Test
- public void deleteWindowMagnification_enabling_expectedValuesAndInvokeCallback() {
+ public void deleteWindowMagnification_enabling_expectedValuesAndInvokeCallback()
+ throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
- mAnimationEndCallback);
+ mAnimationCallback);
mInstrumentation.runOnMainSync(
() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.deleteWindowMagnification(
- mAnimationEndCallback2);
+ mAnimationCallback2);
mCurrentScale.set(mController.getScale());
mCurrentCenterX.set(mController.getCenterX());
mCurrentCenterY.set(mController.getCenterY());
@@ -339,25 +353,25 @@
verifyStartValue(mCenterXCaptor, Float.NaN);
verifyStartValue(mCenterYCaptor, Float.NaN);
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
- verify(mAnimationEndCallback, never()).run();
- verify(mAnimationEndCallback2).run();
+ verify(mAnimationCallback).onResult(false);
+ verify(mAnimationCallback2).onResult(true);
}
@Test
- public void deleteWindowMagnification_disabling_checkStartAndValues() {
+ public void deleteWindowMagnification_disabling_checkStartAndValues() throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
- mAnimationEndCallback);
+ mAnimationCallback);
- deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback2);
+ deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback2);
verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture());
verify(mSpyController).deleteWindowMagnification();
assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
- verify(mAnimationEndCallback, never()).run();
- verify(mAnimationEndCallback2).run();
+ verify(mAnimationCallback).onResult(false);
+ verify(mAnimationCallback2).onResult(true);
}
@Test
@@ -386,22 +400,22 @@
}
private void enableWindowMagnificationAndWaitAnimating(long duration,
- @Nullable Runnable endCallback) {
+ @Nullable IRemoteMagnificationAnimationCallback callback) {
mInstrumentation.runOnMainSync(
() -> {
Mockito.reset(mSpyController);
mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
- DEFAULT_CENTER_X, DEFAULT_CENTER_Y, endCallback);
+ DEFAULT_CENTER_X, DEFAULT_CENTER_Y, callback);
});
SystemClock.sleep(duration);
}
private void deleteWindowMagnificationAndWaitAnimating(long duration,
- @Nullable Runnable endCallback) {
+ @Nullable IRemoteMagnificationAnimationCallback callback) {
mInstrumentation.runOnMainSync(
() -> {
resetMockObjects();
- mWindowMagnificationAnimationController.deleteWindowMagnification(endCallback);
+ mWindowMagnificationAnimationController.deleteWindowMagnification(callback);
});
SystemClock.sleep(duration);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index f1f394e..539b321 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -29,11 +29,14 @@
import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.content.res.Resources;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.View;
+import android.view.WindowManager;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -63,15 +66,28 @@
WindowMagnifierCallback mWindowMagnifierCallback;
@Mock
SurfaceControl.Transaction mTransaction;
- private Context mContext;
+ @Mock
+ private WindowManager mWindowManager;
+ private Resources mResources;
private WindowMagnificationController mWindowMagnificationController;
private Instrumentation mInstrumentation;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext = Mockito.spy(getContext());
+ mContext = Mockito.spy(getContext());
mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ WindowManager wm = mContext.getSystemService(WindowManager.class);
+ doAnswer(invocation ->
+ wm.getMaximumWindowMetrics()
+ ).when(mWindowManager).getMaximumWindowMetrics();
+ mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+ doAnswer(invocation -> {
+ View view = invocation.getArgument(0);
+ WindowManager.LayoutParams lp = invocation.getArgument(1);
+ view.setLayoutParams(lp);
+ return null;
+ }).when(mWindowManager).addView(any(View.class), any(WindowManager.LayoutParams.class));
doAnswer(invocation -> {
FrameCallback callback = invocation.getArgument(0);
callback.doFrame(0);
@@ -81,6 +97,8 @@
when(mTransaction.remove(any())).thenReturn(mTransaction);
when(mTransaction.setGeometry(any(), any(), any(),
anyInt())).thenReturn(mTransaction);
+ mResources = Mockito.spy(mContext.getResources());
+ when(mContext.getResources()).thenReturn(mResources);
mWindowMagnificationController = new WindowMagnificationController(mContext,
mHandler, mSfVsyncFrameProvider,
mMirrorWindowControl, mTransaction, mWindowMagnifierCallback);
@@ -150,4 +168,63 @@
mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
});
}
+
+ @Test
+ public void onOrientationChanged_enabled_updateDisplayRotationAndLayout() {
+ final Display display = Mockito.spy(mContext.getDisplay());
+ when(display.getRotation()).thenReturn(Surface.ROTATION_90);
+ when(mContext.getDisplay()).thenReturn(display);
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN);
+ Mockito.reset(mWindowManager);
+ });
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
+ });
+
+ assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation);
+ verify(mWindowManager).updateViewLayout(any(), any());
+ }
+
+ @Test
+ public void onOrientationChanged_disabled_updateDisplayRotation() {
+ final Display display = Mockito.spy(mContext.getDisplay());
+ when(display.getRotation()).thenReturn(Surface.ROTATION_90);
+ when(mContext.getDisplay()).thenReturn(display);
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
+ });
+
+ assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation);
+ }
+
+
+ @Test
+ public void onDensityChanged_enabled_updateDimensionsAndLayout() {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN);
+ Mockito.reset(mWindowManager);
+ });
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
+ });
+
+ verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt());
+ verify(mWindowManager).removeView(any());
+ verify(mWindowManager).addView(any(), any());
+ }
+
+ @Test
+ public void onDensityChanged_disabled_updateDimensions() {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
+ });
+
+ verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 8f082c1..ade3290 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -47,6 +47,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
import org.junit.Before;
@@ -82,6 +83,8 @@
private PackageManager mPackageManager;
@Mock(stubOnly = true)
private AudioManager mAudioManager;
+ @Mock()
+ private BroadcastDispatcher mDispatcher;
@Mock(stubOnly = true)
private AudioManager.AudioRecordingCallback mRecordingCallback;
@Mock(stubOnly = true)
@@ -120,7 +123,8 @@
mTestableLooper.getLooper(),
mDumpManager,
mFlagsCache,
- mAudioManager
+ mAudioManager,
+ mDispatcher
);
}
@@ -128,12 +132,14 @@
public void testOnlyListenForFewOps() {
mController.setListening(true);
verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsControllerImpl.OPS, mController);
+ verify(mDispatcher, times(1)).registerReceiverWithHandler(eq(mController), any(), any());
}
@Test
public void testStopListening() {
mController.setListening(false);
verify(mAppOpsManager, times(1)).stopWatchingActive(mController);
+ verify(mDispatcher, times(1)).unregisterReceiver(mController);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index c8566c5..88608eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -46,6 +46,7 @@
import android.hardware.biometrics.PromptInfo;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorProperties;
import android.os.Bundle;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -69,6 +70,8 @@
import java.util.List;
import java.util.Random;
+import javax.inject.Provider;
+
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
@@ -82,6 +85,16 @@
private AuthDialog mDialog1;
@Mock
private AuthDialog mDialog2;
+ @Mock
+ private CommandQueue mCommandQueue;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private IActivityTaskManager mActivityTaskManager;
+ @Mock
+ private FingerprintManager mFingerprintManager;
+ @Mock
+ private UdfpsController mUdfpsController;
private TestableAuthController mAuthController;
@@ -104,8 +117,16 @@
when(mDialog1.isAllowDeviceCredentials()).thenReturn(false);
when(mDialog2.isAllowDeviceCredentials()).thenReturn(false);
- mAuthController = new TestableAuthController(context, mock(CommandQueue.class),
- mock(StatusBarStateController.class), new MockInjector());
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ FingerprintSensorProperties prop = new FingerprintSensorProperties(
+ 1, FingerprintSensorProperties.TYPE_UDFPS, true, 1);
+ List<FingerprintSensorProperties> props = new ArrayList<>();
+ props.add(prop);
+ when(mFingerprintManager.getSensorProperties()).thenReturn(props);
+
+ mAuthController = new TestableAuthController(context, mCommandQueue,
+ mStatusBarStateController, mActivityTaskManager, mFingerprintManager,
+ () -> mUdfpsController);
mAuthController.start();
}
@@ -419,7 +440,7 @@
taskInfo.topActivity = mock(ComponentName.class);
when(taskInfo.topActivity.getPackageName()).thenReturn("other_package");
tasks.add(taskInfo);
- when(mAuthController.mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks);
+ when(mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks);
mAuthController.mTaskStackListener.onTaskStackChanged();
waitForIdleSync();
@@ -463,6 +484,27 @@
eq(null) /* credentialAttestation */);
}
+ @Test
+ public void testOnAodInterrupt() {
+ final int pos = 10;
+ mAuthController.onAodInterrupt(pos, pos);
+ verify(mUdfpsController).onAodInterrupt(eq(pos), eq(pos));
+ }
+
+ @Test
+ public void testOnBiometricAuthenticated_OnCancelAodInterrupt() {
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FINGERPRINT);
+ mAuthController.onBiometricAuthenticated();
+ verify(mUdfpsController).onCancelAodInterrupt();
+ }
+
+ @Test
+ public void testOnBiometricError_OnCancelAodInterrupt() {
+ showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FINGERPRINT);
+ mAuthController.onBiometricError(0, 0, 0);
+ verify(mUdfpsController).onCancelAodInterrupt();
+ }
+
// Helpers
private void showDialog(int authenticators, int biometricModality) {
@@ -504,8 +546,12 @@
private PromptInfo mLastBiometricPromptInfo;
TestableAuthController(Context context, CommandQueue commandQueue,
- StatusBarStateController statusBarStateController, Injector injector) {
- super(context, commandQueue, statusBarStateController, injector);
+ StatusBarStateController statusBarStateController,
+ IActivityTaskManager activityTaskManager,
+ FingerprintManager fingerprintManager,
+ Provider<UdfpsController> udfpsControllerFactory) {
+ super(context, commandQueue, statusBarStateController, activityTaskManager,
+ fingerprintManager, udfpsControllerFactory);
}
@Override
@@ -527,17 +573,5 @@
return dialog;
}
}
-
- private final class MockInjector extends AuthController.Injector {
- @Override
- IActivityTaskManager getActivityTaskManager() {
- return mock(IActivityTaskManager.class);
- }
-
- @Override
- FingerprintManager getFingerprintManager(Context context) {
- return mock(FingerprintManager.class);
- }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
new file mode 100644
index 0000000..87ec72f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -0,0 +1,210 @@
+/*
+ * 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.biometrics;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class UdfpsControllerTest extends SysuiTestCase {
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ // Unit under test
+ private UdfpsController mUdfpsController;
+
+ // Dependencies
+ @Mock
+ private Resources mResources;
+ @Mock
+ private LayoutInflater mLayoutInflater;
+ @Mock
+ private FingerprintManager mFingerprintManager;
+ @Mock
+ private PowerManager mPowerManager;
+ @Mock
+ private WindowManager mWindowManager;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ private FakeSettings mSystemSettings;
+ private FakeExecutor mFgExecutor;
+
+ // Stuff for configuring mocks
+ @Mock
+ private UdfpsView mUdfpsView;
+ @Mock
+ private TypedArray mBrightnessValues;
+ @Mock
+ private TypedArray mBrightnessBacklight;
+
+ // Capture listeners so that they can be used to send events
+ @Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor;
+ private IUdfpsOverlayController mOverlayController;
+ @Captor private ArgumentCaptor<UdfpsView.OnTouchListener> mTouchListenerCaptor;
+
+ @Before
+ public void setUp() {
+ setUpResources();
+ when(mLayoutInflater.inflate(R.layout.udfps_view, null, false)).thenReturn(mUdfpsView);
+ mSystemSettings = new FakeSettings();
+ mFgExecutor = new FakeExecutor(new FakeSystemClock());
+ mUdfpsController = new UdfpsController(
+ mContext,
+ mResources,
+ mLayoutInflater,
+ mFingerprintManager,
+ mPowerManager,
+ mWindowManager,
+ mSystemSettings,
+ mStatusBarStateController,
+ mFgExecutor);
+ verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
+ mOverlayController = mOverlayCaptor.getValue();
+ }
+
+ private void setUpResources() {
+ when(mBrightnessValues.length()).thenReturn(2);
+ when(mBrightnessValues.getFloat(0, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(1f);
+ when(mBrightnessValues.getFloat(1, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(2f);
+ when(mResources.obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits))
+ .thenReturn(mBrightnessValues);
+ when(mBrightnessBacklight.length()).thenReturn(2);
+ when(mBrightnessBacklight.getFloat(0, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(1f);
+ when(mBrightnessBacklight.getFloat(1, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(2f);
+ when(mResources.obtainTypedArray(
+ com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
+ .thenReturn(mBrightnessBacklight);
+ when(mResources.getIntArray(com.android.internal.R.array.config_screenBrightnessBacklight))
+ .thenReturn(new int[]{1, 2});
+ }
+
+ @Test
+ public void dozeTimeTick() {
+ mUdfpsController.dozeTimeTick();
+ verify(mUdfpsView).dozeTimeTick();
+ }
+
+ @Test
+ public void showUdfpsOverlay_addsViewToWindow() throws RemoteException {
+ mOverlayController.showUdfpsOverlay();
+ mFgExecutor.runAllReady();
+ verify(mWindowManager).addView(eq(mUdfpsView), any());
+ }
+
+ @Test
+ public void hideUdfpsOverlay_removesViewFromWindow() throws RemoteException {
+ mOverlayController.showUdfpsOverlay();
+ mOverlayController.hideUdfpsOverlay();
+ mFgExecutor.runAllReady();
+ verify(mWindowManager).removeView(eq(mUdfpsView));
+ }
+
+ @Test
+ public void fingerDown() throws RemoteException {
+ // Configure UdfpsView to accept the ACTION_DOWN event
+ when(mUdfpsView.isScrimShowing()).thenReturn(false);
+ when(mUdfpsView.isValidTouch(anyFloat(), anyFloat(), anyFloat())).thenReturn(true);
+
+ // GIVEN that the overlay is showing
+ mOverlayController.showUdfpsOverlay();
+ mFgExecutor.runAllReady();
+ // WHEN ACTION_DOWN is received
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
+ event.recycle();
+ // THEN the event is passed to the FingerprintManager
+ verify(mFingerprintManager).onFingerDown(eq(0), eq(0), eq(0f), eq(0f));
+ // AND the scrim and dot is shown
+ verify(mUdfpsView).showScrimAndDot();
+ }
+
+ @Test
+ public void aodInterrupt() throws RemoteException {
+ // GIVEN that the overlay is showing
+ mOverlayController.showUdfpsOverlay();
+ mFgExecutor.runAllReady();
+ // WHEN fingerprint is requested because of AOD interrupt
+ mUdfpsController.onAodInterrupt(0, 0);
+ // THEN the event is passed to the FingerprintManager
+ verify(mFingerprintManager).onFingerDown(eq(0), eq(0), anyFloat(), anyFloat());
+ // AND the scrim and dot is shown
+ verify(mUdfpsView).showScrimAndDot();
+ }
+
+ @Test
+ public void cancelAodInterrupt() throws RemoteException {
+ // GIVEN AOD interrupt
+ mOverlayController.showUdfpsOverlay();
+ mFgExecutor.runAllReady();
+ mUdfpsController.onAodInterrupt(0, 0);
+ // WHEN it is cancelled
+ mUdfpsController.onCancelAodInterrupt();
+ // THEN the scrim and dot is hidden
+ verify(mUdfpsView).hideScrimAndDot();
+ }
+
+ @Test
+ public void aodInterruptTimeout() throws RemoteException {
+ // GIVEN AOD interrupt
+ mOverlayController.showUdfpsOverlay();
+ mFgExecutor.runAllReady();
+ mUdfpsController.onAodInterrupt(0, 0);
+ // WHEN it times out
+ mFgExecutor.advanceClockToNext();
+ mFgExecutor.runAllReady();
+ // THEN the scrim and dot is hidden
+ verify(mUdfpsView).hideScrimAndDot();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
index c591c1b..9fd9b47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
@@ -59,6 +59,7 @@
when(config.doubleTapSensorType()).thenReturn(null);
when(config.tapSensorType()).thenReturn(null);
when(config.longPressSensorType()).thenReturn(null);
+ when(config.udfpsLongPressSensorType()).thenReturn(null);
when(config.tapGestureEnabled(anyInt())).thenReturn(true);
when(config.tapSensorAvailable()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 3e60e016..be0865d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -58,6 +58,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class DozeScreenBrightnessTest extends SysuiTestCase {
@@ -97,7 +99,7 @@
mAlwaysOnDisplayPolicy.dimmingScrimArray = SENSOR_TO_OPACITY;
mSensor = fakeSensorManager.getFakeLightSensor();
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
- mSensor.getSensor(), mDozeHost, null /* handler */,
+ Optional.of(mSensor.getSensor()), mDozeHost, null /* handler */,
mAlwaysOnDisplayPolicy);
mScreen.onScreenState(Display.STATE_ON);
@@ -113,8 +115,6 @@
@Test
public void testAod_usesLightSensor() {
- mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
- mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.onScreenState(Display.STATE_DOZE);
waitForSensorManager();
@@ -125,8 +125,6 @@
@Test
public void testAod_usesDebugValue() throws Exception {
- mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
- mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.onScreenState(Display.STATE_DOZE);
waitForSensorManager();
@@ -167,7 +165,7 @@
@Test
public void testPulsing_withoutLightSensor_setsAoDDimmingScrimTransparent() throws Exception {
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
- null /* sensor */, mDozeHost, null /* handler */,
+ Optional.empty() /* sensor */, mDozeHost, null /* handler */,
mAlwaysOnDisplayPolicy);
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE);
@@ -179,14 +177,13 @@
}
@Test
- public void testDozingAfterPulsing_pausesLightSensor() throws Exception {
+ public void testScreenOffAfterPulsing_pausesLightSensor() throws Exception {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE);
mScreen.transitionTo(DOZE, DOZE_REQUEST_PULSE);
mScreen.transitionTo(DOZE_REQUEST_PULSE, DOZE_PULSING);
mScreen.transitionTo(DOZE_PULSING, DOZE_PULSE_DONE);
mScreen.transitionTo(DOZE_PULSE_DONE, DOZE);
- mScreen.onScreenState(Display.STATE_DOZE);
waitForSensorManager();
mSensor.sendSensorEvent(1);
@@ -195,21 +192,36 @@
}
@Test
+ public void testOnScreenStateSetBeforeTransition_stillRegistersSensor() {
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mScreen.onScreenState(Display.STATE_DOZE);
+ mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+ waitForSensorManager();
+
+ mSensor.sendSensorEvent(1);
+
+ assertEquals(1, mServiceFake.screenBrightness);
+ }
+
+ @Test
public void testNullSensor() throws Exception {
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
- null /* sensor */, mDozeHost, null /* handler */,
+ Optional.empty() /* sensor */, mDozeHost, null /* handler */,
mAlwaysOnDisplayPolicy);
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
+ mScreen.onScreenState(Display.STATE_DOZE);
+ mScreen.onScreenState(Display.STATE_OFF);
}
@Test
public void testNoBrightnessDeliveredAfterFinish() throws Exception {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+ mScreen.onScreenState(Display.STATE_DOZE);
mScreen.transitionTo(DOZE_AOD, FINISH);
waitForSensorManager();
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 1ed5871..3ae02a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -37,6 +37,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.AuthController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -76,6 +77,8 @@
private DockManager mDockManager;
@Mock
private ProximitySensor.ProximityCheck mProximityCheck;
+ @Mock
+ private AuthController mAuthController;
private DozeTriggers mTriggers;
private FakeSensorManager mSensors;
@@ -100,7 +103,8 @@
mTriggers = new DozeTriggers(mContext, mHost, mAlarmManager, config, parameters,
asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
- mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings());
+ mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings(),
+ mAuthController);
mTriggers.setDozeMachine(mMachine);
waitForSensorManager();
}
@@ -186,6 +190,15 @@
mTriggers.onSensor(DozeLog.REASON_SENSOR_TAP, 100, 100, null);
}
+ @Test
+ public void testOnSensor_Fingerprint() {
+ final int screenX = 100;
+ final int screenY = 100;
+ final int reason = DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS;
+ mTriggers.onSensor(reason, screenX, screenY, null);
+ verify(mAuthController).onAodInterrupt(eq(screenX), eq(screenY));
+ }
+
private void waitForSensorManager() {
mExecutor.runAllReady();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt
index 887cc77..2d90cc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt
@@ -225,7 +225,7 @@
@Test
fun remoteSession_loadedEventNotFiltered() {
- // GIVEN a remove session
+ // GIVEN a remote session
whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo)
val controllers = listOf(controller1)
whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers)
@@ -260,6 +260,22 @@
}
@Test
+ fun remoteAndLocalSessions_remoteSessionWithoutNotification() {
+ // GIVEN remote and local sessions
+ whenever(controller2.getPlaybackInfo()).thenReturn(remotePlaybackInfo)
+ val controllers = listOf(controller1, controller2)
+ whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers)
+ sessionListener.onActiveSessionsChanged(controllers)
+ // WHEN a loaded event is received that matches the local session
+ filter.onMediaDataLoaded(KEY, null, mediaData1)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ // THEN the event is not filtered because there isn't a notification for the remote
+ // session.
+ verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1))
+ }
+
+ @Test
fun remoteAndLocalHaveDifferentKeys_localLoadedEventFiltered() {
// GIVEN remote and local sessions
val key1 = "KEY_1"
@@ -285,6 +301,29 @@
}
@Test
+ fun remoteAndLocalHaveDifferentKeys_remoteSessionWithoutNotification() {
+ // GIVEN remote and local sessions
+ val key1 = "KEY_1"
+ val key2 = "KEY_2"
+ whenever(controller2.getPlaybackInfo()).thenReturn(remotePlaybackInfo)
+ val controllers = listOf(controller1, controller2)
+ whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers)
+ sessionListener.onActiveSessionsChanged(controllers)
+ // WHEN a loaded event is received that matches the local session
+ filter.onMediaDataLoaded(key1, null, mediaData1)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ // THEN the event is not filtered
+ verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1))
+ // WHEN a loaded event is received that matches the remote session
+ filter.onMediaDataLoaded(key2, null, mediaData2)
+ bgExecutor.runAllReady()
+ fgExecutor.runAllReady()
+ // THEN the event is not filtered
+ verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2))
+ }
+
+ @Test
fun multipleRemoteSessions_loadedEventNotFiltered() {
// GIVEN two remote sessions
whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo)
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
new file mode 100644
index 0000000..0e376bd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -0,0 +1,200 @@
+/*
+ * 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.FrameLayout;
+
+import androidx.core.graphics.drawable.IconCompat;
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.media.LocalMediaManager;
+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 MediaOutputAdapterTest 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";
+
+ // 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 MediaOutputAdapter mMediaOutputAdapter;
+ private MediaOutputAdapter.MediaDeviceViewHolder mViewHolder;
+ private List<MediaDevice> mMediaDevices = new ArrayList<>();
+
+ @Before
+ public void setUp() {
+ mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
+ mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
+ .onCreateViewHolder(new FrameLayout(mContext), 0);
+
+ when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices);
+ when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false);
+ when(mMediaOutputController.isZeroMode()).thenReturn(false);
+ when(mMediaOutputController.isTransferring()).thenReturn(false);
+ when(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).thenReturn(mIconCompat);
+ when(mMediaOutputController.getDeviceIconCompat(mMediaDevice2)).thenReturn(mIconCompat);
+ when(mMediaOutputController.getCurrentConnectedMediaDevice()).thenReturn(mMediaDevice1);
+ 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);
+ when(mMediaDevice1.getState()).thenReturn(
+ LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
+ when(mMediaDevice2.getState()).thenReturn(
+ LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
+ mMediaDevices.add(mMediaDevice1);
+ mMediaDevices.add(mMediaDevice2);
+ }
+
+ @Test
+ public void getItemCount_nonZeroMode_isDeviceSize() {
+ assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaDevices.size());
+ }
+
+ @Test
+ public void getItemCount_zeroMode_containExtraOneForPairNew() {
+ when(mMediaOutputController.isZeroMode()).thenReturn(true);
+
+ assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaDevices.size() + 1);
+ }
+
+ @Test
+ public void onBindViewHolder_zeroMode_bindPairNew_verifyView() {
+ when(mMediaOutputController.isZeroMode()).thenReturn(true);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
+
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTitleText.getText()).isEqualTo(mContext.getText(
+ R.string.media_output_dialog_pairing_new));
+ }
+
+ @Test
+ public void onBindViewHolder_bindConnectedDevice_verifyView() {
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
+ }
+
+ @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.mTwoLineLayout.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));
+ }
+
+ @Test
+ public void onBindViewHolder_bindFailedStateDevice_verifyView() {
+ when(mMediaDevice2.getState()).thenReturn(
+ LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mProgressBar.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(
+ R.string.media_output_dialog_connect_failed));
+ assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2);
+ }
+
+ @Test
+ public void onBindViewHolder_inTransferring_bindTransferringDevice_verifyView() {
+ when(mMediaOutputController.isTransferring()).thenReturn(true);
+ when(mMediaDevice1.getState()).thenReturn(
+ LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
+ }
+
+ @Test
+ public void onBindViewHolder_inTransferring_bindNonTransferringDevice_verifyView() {
+ when(mMediaOutputController.isTransferring()).thenReturn(true);
+ when(mMediaDevice2.getState()).thenReturn(
+ LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
+ }
+
+ @Test
+ public void onItemClick_clickPairNew_verifyLaunchBluetoothPairing() {
+ when(mMediaOutputController.isZeroMode()).thenReturn(true);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
+ mViewHolder.mFrameLayout.performClick();
+
+ verify(mMediaOutputController).launchBluetoothPairing();
+ }
+
+ @Test
+ public void onItemClick_clickDevice_verifyConnectDevice() {
+ assertThat(mMediaDevice2.getState()).isEqualTo(
+ LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+ mViewHolder.mFrameLayout.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
new file mode 100644
index 0000000..42b21c6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -0,0 +1,212 @@
+/*
+ * 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.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.media.session.MediaSessionManager;
+import android.os.Bundle;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.core.graphics.drawable.IconCompat;
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+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.phone.ShadeController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class MediaOutputBaseDialogTest extends SysuiTestCase {
+
+ private static final String TEST_PACKAGE = "test_package";
+
+ // Mock
+ private MediaOutputBaseAdapter mMediaOutputBaseAdapter = mock(MediaOutputBaseAdapter.class);
+
+ 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 MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl;
+ private MediaOutputController mMediaOutputController;
+ private int mHeaderIconRes;
+ private IconCompat mIconCompat;
+ private CharSequence mHeaderTitle;
+ private CharSequence mHeaderSubtitle;
+
+ @Before
+ public void setUp() {
+ mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
+ mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
+ mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext,
+ mMediaOutputController);
+ mMediaOutputBaseDialogImpl.onCreate(new Bundle());
+ }
+
+ @Test
+ public void refresh_withIconRes_iconIsVisible() {
+ mHeaderIconRes = 1;
+ mMediaOutputBaseDialogImpl.refresh();
+ final ImageView view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.header_icon);
+
+ assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void refresh_withIconCompat_iconIsVisible() {
+ mIconCompat = mock(IconCompat.class);
+ mMediaOutputBaseDialogImpl.refresh();
+ final ImageView view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.header_icon);
+
+ assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void refresh_noIcon_iconLayoutNotVisible() {
+ mHeaderIconRes = 0;
+ mIconCompat = null;
+ mMediaOutputBaseDialogImpl.refresh();
+ final ImageView view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.header_icon);
+
+ assertThat(view.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void refresh_checkTitle() {
+ mHeaderTitle = "test_string";
+
+ mMediaOutputBaseDialogImpl.refresh();
+ final TextView titleView = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.header_title);
+
+ assertThat(titleView.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(titleView.getText()).isEqualTo(mHeaderTitle);
+ }
+
+ @Test
+ public void refresh_withSubtitle_checkSubtitle() {
+ mHeaderSubtitle = "test_string";
+
+ mMediaOutputBaseDialogImpl.refresh();
+ final TextView subtitleView = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.header_subtitle);
+
+ assertThat(subtitleView.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(subtitleView.getText()).isEqualTo(mHeaderSubtitle);
+ }
+
+ @Test
+ public void refresh_noSubtitle_checkSubtitle() {
+ mMediaOutputBaseDialogImpl.refresh();
+ final TextView subtitleView = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.header_subtitle);
+
+ assertThat(subtitleView.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void refresh_inDragging_notUpdateAdapter() {
+ when(mMediaOutputBaseAdapter.isDragging()).thenReturn(true);
+ mMediaOutputBaseDialogImpl.refresh();
+
+ verify(mMediaOutputBaseAdapter, never()).notifyDataSetChanged();
+ }
+
+ @Test
+ public void refresh_notInDragging_verifyUpdateAdapter() {
+ when(mMediaOutputBaseAdapter.isDragging()).thenReturn(false);
+ mMediaOutputBaseDialogImpl.refresh();
+
+ verify(mMediaOutputBaseAdapter).notifyDataSetChanged();
+ }
+
+ @Test
+ public void refresh_with6Devices_checkBottomPaddingVisibility() {
+ for (int i = 0; i < 6; i++) {
+ mMediaOutputController.mMediaDevices.add(mock(MediaDevice.class));
+ }
+ mMediaOutputBaseDialogImpl.refresh();
+ final View view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.list_bottom_padding);
+
+ assertThat(view.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void refresh_with5Devices_checkBottomPaddingVisibility() {
+ for (int i = 0; i < 5; i++) {
+ mMediaOutputController.mMediaDevices.add(mock(MediaDevice.class));
+ }
+ mMediaOutputBaseDialogImpl.refresh();
+ final View view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.list_bottom_padding);
+
+ assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ class MediaOutputBaseDialogImpl extends MediaOutputBaseDialog {
+
+ MediaOutputBaseDialogImpl(Context context, MediaOutputController mediaOutputController) {
+ super(context, mediaOutputController);
+
+ mAdapter = mMediaOutputBaseAdapter;
+ }
+
+ int getHeaderIconRes() {
+ return mHeaderIconRes;
+ }
+
+ IconCompat getHeaderIcon() {
+ return mIconCompat;
+ }
+
+ int getHeaderIconSize() {
+ return 10;
+ }
+
+ CharSequence getHeaderText() {
+ return mHeaderTitle;
+ }
+
+ CharSequence getHeaderSubtitle() {
+ return mHeaderSubtitle;
+ }
+ }
+}
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
new file mode 100644
index 0000000..0dcdecf
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -0,0 +1,348 @@
+/*
+ * 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.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.media.MediaDescription;
+import android.media.MediaMetadata;
+import android.media.RoutingSessionInfo;
+import android.media.session.MediaController;
+import android.media.session.MediaSessionManager;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+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.phone.ShadeController;
+
+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 MediaOutputControllerTest extends SysuiTestCase {
+
+ 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_ARTIST = "test_artist";
+ private static final String TEST_SONG = "test_song";
+ private static final String TEST_SESSION_ID = "test_session_id";
+ private static final String TEST_SESSION_NAME = "test_session_name";
+ // Mock
+ private MediaController mMediaController = mock(MediaController.class);
+ private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
+ private CachedBluetoothDeviceManager mCachedBluetoothDeviceManager =
+ mock(CachedBluetoothDeviceManager.class);
+ private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
+ private MediaOutputController.Callback mCb = mock(MediaOutputController.Callback.class);
+ private MediaDevice mMediaDevice1 = mock(MediaDevice.class);
+ private MediaDevice mMediaDevice2 = mock(MediaDevice.class);
+ private MediaMetadata mMediaMetadata = mock(MediaMetadata.class);
+ private RoutingSessionInfo mRemoteSessionInfo = mock(RoutingSessionInfo.class);
+ private ShadeController mShadeController = mock(ShadeController.class);
+ private ActivityStarter mStarter = mock(ActivityStarter.class);
+
+ private Context mSpyContext;
+ private MediaOutputController mMediaOutputController;
+ private LocalMediaManager mLocalMediaManager;
+ private List<MediaController> mMediaControllers = new ArrayList<>();
+ private List<MediaDevice> mMediaDevices = new ArrayList<>();
+ private MediaDescription mMediaDescription;
+ private List<RoutingSessionInfo> mRoutingSessionInfos = new ArrayList<>();
+
+ @Before
+ public void setUp() {
+ mSpyContext = spy(mContext);
+ when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ mMediaControllers.add(mMediaController);
+ when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers);
+ doReturn(mMediaSessionManager).when(mSpyContext).getSystemService(
+ MediaSessionManager.class);
+ when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(
+ mCachedBluetoothDeviceManager);
+ mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME,
+ mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
+ mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
+ mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
+ MediaDescription.Builder builder = new MediaDescription.Builder();
+ builder.setTitle(TEST_SONG);
+ builder.setSubtitle(TEST_ARTIST);
+ mMediaDescription = builder.build();
+ when(mMediaMetadata.getDescription()).thenReturn(mMediaDescription);
+ when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID);
+ when(mMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID);
+ mMediaDevices.add(mMediaDevice1);
+ mMediaDevices.add(mMediaDevice2);
+ }
+
+ @Test
+ public void start_verifyLocalMediaManagerInit() {
+ mMediaOutputController.start(mCb);
+
+ verify(mLocalMediaManager).registerCallback(mMediaOutputController);
+ verify(mLocalMediaManager).startScan();
+ }
+
+ @Test
+ public void stop_verifyLocalMediaManagerDeinit() {
+ mMediaOutputController.start(mCb);
+ reset(mLocalMediaManager);
+
+ mMediaOutputController.stop();
+
+ verify(mLocalMediaManager).unregisterCallback(mMediaOutputController);
+ verify(mLocalMediaManager).stopScan();
+ }
+
+ @Test
+ public void start_withPackageName_verifyMediaControllerInit() {
+ mMediaOutputController.start(mCb);
+
+ verify(mMediaController).registerCallback(any());
+ }
+
+ @Test
+ public void start_withoutPackageName_verifyMediaControllerInit() {
+ mMediaOutputController = new MediaOutputController(mSpyContext, null, mMediaSessionManager,
+ mLocalBluetoothManager, mShadeController, mStarter);
+
+ mMediaOutputController.start(mCb);
+
+ verify(mMediaController, never()).registerCallback(any());
+ }
+
+ @Test
+ public void stop_withPackageName_verifyMediaControllerDeinit() {
+ mMediaOutputController.start(mCb);
+ reset(mMediaController);
+
+ mMediaOutputController.stop();
+
+ verify(mMediaController).unregisterCallback(any());
+ }
+
+ @Test
+ public void stop_withoutPackageName_verifyMediaControllerDeinit() {
+ mMediaOutputController = new MediaOutputController(mSpyContext, null, mMediaSessionManager,
+ mLocalBluetoothManager, mShadeController, mStarter);
+ mMediaOutputController.start(mCb);
+
+ mMediaOutputController.stop();
+
+ verify(mMediaController, never()).unregisterCallback(any());
+ }
+
+ @Test
+ public void onDeviceListUpdate_verifyDeviceListCallback() {
+ mMediaOutputController.start(mCb);
+ reset(mCb);
+
+ mMediaOutputController.onDeviceListUpdate(mMediaDevices);
+ final List<MediaDevice> devices = new ArrayList<>(mMediaOutputController.getMediaDevices());
+
+ assertThat(devices.containsAll(mMediaDevices)).isTrue();
+ assertThat(devices.size()).isEqualTo(mMediaDevices.size());
+ verify(mCb).onRouteChanged();
+ }
+
+ @Test
+ public void onSelectedDeviceStateChanged_verifyCallback() {
+ mMediaOutputController.start(mCb);
+ reset(mCb);
+
+ mMediaOutputController.onSelectedDeviceStateChanged(mMediaDevice1,
+ LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
+
+ verify(mCb).onRouteChanged();
+ }
+
+ @Test
+ public void onDeviceAttributesChanged_verifyCallback() {
+ mMediaOutputController.start(mCb);
+ reset(mCb);
+
+ mMediaOutputController.onDeviceAttributesChanged();
+
+ verify(mCb).onRouteChanged();
+ }
+
+ @Test
+ public void onRequestFailed_verifyCallback() {
+ mMediaOutputController.start(mCb);
+ reset(mCb);
+
+ mMediaOutputController.onRequestFailed(0 /* reason */);
+
+ verify(mCb).onRouteChanged();
+ }
+
+ @Test
+ public void getHeaderTitle_withoutMetadata_returnDefaultString() {
+ when(mMediaController.getMetadata()).thenReturn(null);
+
+ mMediaOutputController.start(mCb);
+
+ assertThat(mMediaOutputController.getHeaderTitle()).isEqualTo(
+ mContext.getText(R.string.controls_media_title));
+ }
+
+ @Test
+ public void getHeaderTitle_withMetadata_returnSongName() {
+ when(mMediaController.getMetadata()).thenReturn(mMediaMetadata);
+
+ mMediaOutputController.start(mCb);
+
+ assertThat(mMediaOutputController.getHeaderTitle()).isEqualTo(TEST_SONG);
+ }
+
+ @Test
+ public void getHeaderSubTitle_withoutMetadata_returnNull() {
+ when(mMediaController.getMetadata()).thenReturn(null);
+
+ mMediaOutputController.start(mCb);
+
+ assertThat(mMediaOutputController.getHeaderSubTitle()).isNull();
+ }
+
+ @Test
+ public void getHeaderSubTitle_withMetadata_returnArtistName() {
+ when(mMediaController.getMetadata()).thenReturn(mMediaMetadata);
+
+ mMediaOutputController.start(mCb);
+
+ assertThat(mMediaOutputController.getHeaderSubTitle()).isEqualTo(TEST_ARTIST);
+ }
+
+ @Test
+ public void connectDevice_verifyConnect() {
+ mMediaOutputController.connectDevice(mMediaDevice1);
+
+ // Wait for background thread execution
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ verify(mLocalMediaManager).connectDevice(mMediaDevice1);
+ }
+
+ @Test
+ public void getActiveRemoteMediaDevice_isSystemSession_returnSession() {
+ when(mRemoteSessionInfo.getId()).thenReturn(TEST_SESSION_ID);
+ when(mRemoteSessionInfo.getName()).thenReturn(TEST_SESSION_NAME);
+ when(mRemoteSessionInfo.getVolumeMax()).thenReturn(100);
+ when(mRemoteSessionInfo.getVolume()).thenReturn(10);
+ when(mRemoteSessionInfo.isSystemSession()).thenReturn(false);
+ mRoutingSessionInfos.add(mRemoteSessionInfo);
+ when(mLocalMediaManager.getActiveMediaSession()).thenReturn(mRoutingSessionInfos);
+
+ assertThat(mMediaOutputController.getActiveRemoteMediaDevices()).containsExactly(
+ mRemoteSessionInfo);
+ }
+
+ @Test
+ public void getActiveRemoteMediaDevice_notSystemSession_returnEmpty() {
+ when(mRemoteSessionInfo.getId()).thenReturn(TEST_SESSION_ID);
+ when(mRemoteSessionInfo.getName()).thenReturn(TEST_SESSION_NAME);
+ when(mRemoteSessionInfo.getVolumeMax()).thenReturn(100);
+ when(mRemoteSessionInfo.getVolume()).thenReturn(10);
+ when(mRemoteSessionInfo.isSystemSession()).thenReturn(true);
+ mRoutingSessionInfos.add(mRemoteSessionInfo);
+ when(mLocalMediaManager.getActiveMediaSession()).thenReturn(mRoutingSessionInfos);
+
+ assertThat(mMediaOutputController.getActiveRemoteMediaDevices()).isEmpty();
+ }
+
+ @Test
+ public void isZeroMode_onlyFromPhoneOutput_returnTrue() {
+ // Multiple available devices
+ assertThat(mMediaOutputController.isZeroMode()).isFalse();
+ when(mMediaDevice1.getDeviceType()).thenReturn(
+ MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE);
+ mMediaDevices.clear();
+ mMediaDevices.add(mMediaDevice1);
+ mMediaOutputController.start(mCb);
+ mMediaOutputController.onDeviceListUpdate(mMediaDevices);
+
+ assertThat(mMediaOutputController.isZeroMode()).isTrue();
+
+ when(mMediaDevice1.getDeviceType()).thenReturn(
+ MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE);
+
+ assertThat(mMediaOutputController.isZeroMode()).isTrue();
+
+ when(mMediaDevice1.getDeviceType()).thenReturn(
+ MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE);
+
+ assertThat(mMediaOutputController.isZeroMode()).isTrue();
+ }
+
+ @Test
+ public void isZeroMode_notFromPhoneOutput_returnFalse() {
+ when(mMediaDevice1.getDeviceType()).thenReturn(
+ MediaDevice.MediaDeviceType.TYPE_UNKNOWN);
+ mMediaDevices.clear();
+ mMediaDevices.add(mMediaDevice1);
+ mMediaOutputController.start(mCb);
+ mMediaOutputController.onDeviceListUpdate(mMediaDevices);
+
+ assertThat(mMediaOutputController.isZeroMode()).isFalse();
+
+ when(mMediaDevice1.getDeviceType()).thenReturn(
+ MediaDevice.MediaDeviceType.TYPE_FAST_PAIR_BLUETOOTH_DEVICE);
+
+ assertThat(mMediaOutputController.isZeroMode()).isFalse();
+
+ when(mMediaDevice1.getDeviceType()).thenReturn(
+ MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
+
+ assertThat(mMediaOutputController.isZeroMode()).isFalse();
+
+ when(mMediaDevice1.getDeviceType()).thenReturn(
+ MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE);
+
+ assertThat(mMediaOutputController.isZeroMode()).isFalse();
+
+ when(mMediaDevice1.getDeviceType()).thenReturn(
+ MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE);
+
+ assertThat(mMediaOutputController.isZeroMode()).isFalse();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
index f0066ba..6f5cbe2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
@@ -81,7 +81,7 @@
@Test
public void getAnimator_withBounds_returnBoundsAnimator() {
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
- .getAnimator(mLeash, new Rect(), new Rect(), null);
+ .getAnimator(mLeash, new Rect(), new Rect(), null, TRANSITION_DIRECTION_TO_PIP);
assertEquals("Expect ANIM_TYPE_BOUNDS animation",
animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
@@ -93,12 +93,12 @@
final Rect endValue1 = new Rect(100, 100, 200, 200);
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
- .getAnimator(mLeash, startValue, endValue1, null);
+ .getAnimator(mLeash, startValue, endValue1, null, TRANSITION_DIRECTION_TO_PIP);
oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
oldAnimator.start();
final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
- .getAnimator(mLeash, startValue, endValue2, null);
+ .getAnimator(mLeash, startValue, endValue2, null, TRANSITION_DIRECTION_TO_PIP);
assertEquals("getAnimator with same type returns same animator",
oldAnimator, newAnimator);
@@ -128,7 +128,7 @@
final Rect endValue1 = new Rect(100, 100, 200, 200);
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
- .getAnimator(mLeash, startValue, endValue1, null);
+ .getAnimator(mLeash, startValue, endValue1, null, TRANSITION_DIRECTION_TO_PIP);
animator.updateEndValue(endValue2);
@@ -140,7 +140,7 @@
final Rect startValue = new Rect(0, 0, 100, 100);
final Rect endValue = new Rect(100, 100, 200, 200);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
- .getAnimator(mLeash, startValue, endValue, null);
+ .getAnimator(mLeash, startValue, endValue, null, TRANSITION_DIRECTION_TO_PIP);
animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
animator.setPipAnimationCallback(mPipAnimationCallback);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java
new file mode 100644
index 0000000..a5dead0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java
@@ -0,0 +1,265 @@
+/*
+ * 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.customize;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.Bundle;
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class TileAdapterDelegateTest extends SysuiTestCase {
+
+ private static final int MOVE_TO_POSITION_ID = R.id.accessibility_action_qs_move_to_position;
+ private static final int ADD_TO_POSITION_ID = R.id.accessibility_action_qs_add_to_position;
+ private static final int POSITION_STRING_ID = R.string.accessibility_qs_edit_position;
+
+ @Mock
+ private TileAdapter.Holder mHolder;
+
+ private AccessibilityNodeInfoCompat mInfo;
+ private TileAdapterDelegate mDelegate;
+ private View mView;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mView = new View(mContext);
+ mDelegate = new TileAdapterDelegate();
+ mInfo = AccessibilityNodeInfoCompat.obtain();
+ }
+
+ @Test
+ public void testInfoNoSpecialActionsWhenNoHolder() {
+ mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+ for (AccessibilityNodeInfoCompat.AccessibilityActionCompat action : mInfo.getActionList()) {
+ if (action.getId() == MOVE_TO_POSITION_ID || action.getId() == ADD_TO_POSITION_ID
+ || action.getId() == AccessibilityNodeInfo.ACTION_CLICK) {
+ fail("It should not have special action " + action.getId());
+ }
+ }
+ }
+
+ @Test
+ public void testInfoNoSpecialActionsWhenCannotStartAccessibleAction() {
+ mView.setTag(mHolder);
+ when(mHolder.canTakeAccessibleAction()).thenReturn(false);
+ mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+ for (AccessibilityNodeInfoCompat.AccessibilityActionCompat action : mInfo.getActionList()) {
+ if (action.getId() == MOVE_TO_POSITION_ID || action.getId() == ADD_TO_POSITION_ID
+ || action.getId() == AccessibilityNodeInfo.ACTION_CLICK) {
+ fail("It should not have special action " + action.getId());
+ }
+ }
+ }
+
+ @Test
+ public void testNoCollectionItemInfo() {
+ mInfo.setCollectionItemInfo(
+ AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(0, 1, 0, 1, false));
+
+ mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+ assertThat(mInfo.getCollectionItemInfo()).isNull();
+ }
+
+ @Test
+ public void testStateDescriptionHasPositionForCurrentTile() {
+ mView.setTag(mHolder);
+ int position = 3;
+ when(mHolder.getLayoutPosition()).thenReturn(position);
+ when(mHolder.isCurrentTile()).thenReturn(true);
+ when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+
+ String expectedString = mContext.getString(POSITION_STRING_ID, position);
+
+ mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+ assertThat(mInfo.getStateDescription()).isEqualTo(expectedString);
+ }
+
+ @Test
+ public void testStateDescriptionEmptyForNotCurrentTile() {
+ mView.setTag(mHolder);
+ int position = 3;
+ when(mHolder.getLayoutPosition()).thenReturn(position);
+ when(mHolder.isCurrentTile()).thenReturn(false);
+ when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+
+ mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+ assertThat(mInfo.getStateDescription()).isEqualTo("");
+ }
+
+ @Test
+ public void testClickAddAction() {
+ mView.setTag(mHolder);
+ when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+ when(mHolder.canAdd()).thenReturn(true);
+ when(mHolder.canRemove()).thenReturn(false);
+
+ mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+
+ String expectedString = mContext.getString(R.string.accessibility_qs_edit_tile_add_action);
+ AccessibilityNodeInfoCompat.AccessibilityActionCompat action =
+ getActionForId(mInfo, AccessibilityNodeInfo.ACTION_CLICK);
+ assertThat(action.getLabel().toString()).contains(expectedString);
+ }
+
+ @Test
+ public void testClickRemoveAction() {
+ mView.setTag(mHolder);
+ when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+ when(mHolder.canAdd()).thenReturn(false);
+ when(mHolder.canRemove()).thenReturn(true);
+
+ mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+
+ String expectedString = mContext.getString(
+ R.string.accessibility_qs_edit_remove_tile_action);
+ AccessibilityNodeInfoCompat.AccessibilityActionCompat action =
+ getActionForId(mInfo, AccessibilityNodeInfo.ACTION_CLICK);
+ assertThat(action.getLabel().toString()).contains(expectedString);
+ }
+
+ @Test
+ public void testNoClickAction() {
+ mView.setTag(mHolder);
+ when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+ when(mHolder.canAdd()).thenReturn(false);
+ when(mHolder.canRemove()).thenReturn(false);
+ mInfo.addAction(AccessibilityNodeInfo.ACTION_CLICK);
+
+ mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+
+ AccessibilityNodeInfoCompat.AccessibilityActionCompat action =
+ getActionForId(mInfo, AccessibilityNodeInfo.ACTION_CLICK);
+ assertThat(action).isNull();
+ }
+
+ @Test
+ public void testAddToPositionAction() {
+ mView.setTag(mHolder);
+ when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+ when(mHolder.canAdd()).thenReturn(true);
+
+ mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+ assertThat(getActionForId(mInfo, ADD_TO_POSITION_ID)).isNotNull();
+ }
+
+ @Test
+ public void testNoAddToPositionAction() {
+ mView.setTag(mHolder);
+ when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+ when(mHolder.canAdd()).thenReturn(false);
+
+ mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+ assertThat(getActionForId(mInfo, ADD_TO_POSITION_ID)).isNull();
+ }
+
+ @Test
+ public void testMoveToPositionAction() {
+ mView.setTag(mHolder);
+ when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+ when(mHolder.isCurrentTile()).thenReturn(true);
+
+ mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+ assertThat(getActionForId(mInfo, MOVE_TO_POSITION_ID)).isNotNull();
+ }
+
+ @Test
+ public void testNoMoveToPositionAction() {
+ mView.setTag(mHolder);
+ when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+ when(mHolder.isCurrentTile()).thenReturn(false);
+
+ mDelegate.onInitializeAccessibilityNodeInfo(mView, mInfo);
+ assertThat(getActionForId(mInfo, MOVE_TO_POSITION_ID)).isNull();
+ }
+
+ @Test
+ public void testNoInteractionsWhenCannotTakeAccessibleAction() {
+ mView.setTag(mHolder);
+ when(mHolder.canTakeAccessibleAction()).thenReturn(false);
+
+ mDelegate.performAccessibilityAction(mView, AccessibilityNodeInfo.ACTION_CLICK, null);
+ mDelegate.performAccessibilityAction(mView, MOVE_TO_POSITION_ID, new Bundle());
+ mDelegate.performAccessibilityAction(mView, ADD_TO_POSITION_ID, new Bundle());
+
+ verify(mHolder, never()).toggleState();
+ verify(mHolder, never()).startAccessibleAdd();
+ verify(mHolder, never()).startAccessibleMove();
+ }
+
+ @Test
+ public void testClickActionTogglesState() {
+ mView.setTag(mHolder);
+ when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+
+ mDelegate.performAccessibilityAction(mView, AccessibilityNodeInfo.ACTION_CLICK, null);
+
+ verify(mHolder).toggleState();
+ }
+
+ @Test
+ public void testAddToPositionActionStartsAccessibleAdd() {
+ mView.setTag(mHolder);
+ when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+
+ mDelegate.performAccessibilityAction(mView, ADD_TO_POSITION_ID, null);
+
+ verify(mHolder).startAccessibleAdd();
+ }
+
+ @Test
+ public void testMoveToPositionActionStartsAccessibleMove() {
+ mView.setTag(mHolder);
+ when(mHolder.canTakeAccessibleAction()).thenReturn(true);
+
+ mDelegate.performAccessibilityAction(mView, MOVE_TO_POSITION_ID, null);
+
+ verify(mHolder).startAccessibleMove();
+ }
+
+ private AccessibilityNodeInfoCompat.AccessibilityActionCompat getActionForId(
+ AccessibilityNodeInfoCompat info, int action) {
+ for (AccessibilityNodeInfoCompat.AccessibilityActionCompat a : info.getActionList()) {
+ if (a.getId() == action) {
+ return a;
+ }
+ }
+ return null;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 53ef866..2ef7c65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -34,6 +34,7 @@
import static org.mockito.Mockito.when;
import android.Manifest;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -47,8 +48,11 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.logging.InstanceId;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.qs.DetailAdapter;
+import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -68,6 +72,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -116,11 +121,9 @@
doAnswer(invocation -> {
String spec = (String) invocation.getArguments()[0];
if (FACTORY_TILES.contains(spec)) {
- QSTile m = mock(QSTile.class);
- when(m.isAvailable()).thenReturn(true);
- when(m.getTileSpec()).thenReturn(spec);
- when(m.getState()).thenReturn(mState);
- return m;
+ FakeQSTile tile = new FakeQSTile(mBgExecutor, mMainExecutor);
+ tile.setState(mState);
+ return tile;
} else {
return null;
}
@@ -292,4 +295,132 @@
verifier.verify(t).setTileSpec("hotspot");
verifier.verify(t).destroy();
}
+
+ private static class FakeQSTile implements QSTile {
+
+ private String mSpec = "";
+ private List<Callback> mCallbacks = new ArrayList<>();
+ private boolean mRefreshed;
+ private boolean mListening;
+ private State mState = new State();
+ private final Executor mBgExecutor;
+ private final Executor mMainExecutor;
+
+ FakeQSTile(Executor bgExecutor, Executor mainExecutor) {
+ mBgExecutor = bgExecutor;
+ mMainExecutor = mainExecutor;
+ }
+
+ @Override
+ public String getTileSpec() {
+ return mSpec;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public void setTileSpec(String tileSpec) {
+ mSpec = tileSpec;
+ }
+
+ public void setState(State state) {
+ mState = state;
+ notifyChangedState(mState);
+ }
+
+ @Override
+ public void refreshState() {
+ mBgExecutor.execute(() -> {
+ mRefreshed = true;
+ notifyChangedState(mState);
+ });
+ }
+
+ private void notifyChangedState(State state) {
+ List<Callback> callbacks = new ArrayList<>(mCallbacks);
+ callbacks.forEach(callback -> callback.onStateChanged(state));
+ }
+
+ @Override
+ public void addCallback(Callback callback) {
+ mCallbacks.add(callback);
+ }
+
+ @Override
+ public void removeCallback(Callback callback) {
+ mCallbacks.remove(callback);
+ }
+
+ @Override
+ public void removeCallbacks() {
+ mCallbacks.clear();
+ }
+
+ @Override
+ public void setListening(Object client, boolean listening) {
+ if (listening) {
+ mMainExecutor.execute(() -> {
+ mListening = true;
+ refreshState();
+ });
+ }
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ return mSpec;
+ }
+
+ @Override
+ public State getState() {
+ return mState;
+ }
+
+ @Override
+ public boolean isTileReady() {
+ return mListening && mRefreshed;
+ }
+
+ @Override
+ public QSIconView createTileView(Context context) {
+ return null;
+ }
+
+ @Override
+ public void click() {}
+
+ @Override
+ public void secondaryClick() {}
+
+ @Override
+ public void longClick() {}
+
+ @Override
+ public void userSwitch(int currentUser) {}
+
+ @Override
+ public int getMetricsCategory() {
+ return 0;
+ }
+
+ @Override
+ public InstanceId getInstanceId() {
+ return null;
+ }
+
+ @Override
+ public void setDetailListening(boolean show) {}
+
+ @Override
+ public void destroy() {}
+
+
+ @Override
+ public DetailAdapter getDetailAdapter() {
+ return null;
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index f29b46c..8f1d71c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -54,7 +54,6 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -71,7 +70,6 @@
boolean mHeadsUpAnimatingAway = false;
@Rule public MockitoRule mockito = MockitoJUnit.rule();
- @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
@Before
public void setUp() throws Exception {
@@ -83,9 +81,6 @@
mGroupRow = mNotificationTestHelper.createGroup();
mGroupRow.setHeadsUpAnimatingAwayListener(
animatingAway -> mHeadsUpAnimatingAway = animatingAway);
- mDependency.injectTestDependency(
- NotificationBlockingHelperManager.class,
- mBlockingHelperManager);
}
@Test
@@ -257,30 +252,6 @@
}
@Test
- public void testPerformDismissWithBlockingHelper_falseWhenBlockingHelperIsntShown() {
- when(mBlockingHelperManager.perhapsShowBlockingHelper(
- eq(mGroupRow), any(NotificationMenuRowPlugin.class))).thenReturn(false);
-
- assertFalse(
- mGroupRow.performDismissWithBlockingHelper(false /* fromAccessibility */));
- }
-
- @Test
- public void testPerformDismissWithBlockingHelper_doesntPerformOnGroupSummary() {
- ExpandableNotificationRow childRow = mGroupRow.getChildrenContainer().getViewAtPosition(0);
- when(mBlockingHelperManager.perhapsShowBlockingHelper(eq(childRow), any(NotificationMenuRowPlugin.class)))
- .thenReturn(true);
-
- assertTrue(
- childRow.performDismissWithBlockingHelper(false /* fromAccessibility */));
-
- verify(mBlockingHelperManager, times(1))
- .perhapsShowBlockingHelper(eq(childRow), any(NotificationMenuRowPlugin.class));
- verify(mBlockingHelperManager, times(0))
- .perhapsShowBlockingHelper(eq(mGroupRow), any(NotificationMenuRowPlugin.class));
- }
-
- @Test
public void testIsBlockingHelperShowing_isCorrectlyUpdated() {
mGroupRow.setBlockingHelperShowing(true);
assertTrue(mGroupRow.isBlockingHelperShowing());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index d795cba..cb3e5e26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -73,10 +73,10 @@
TestableLooper.get(this));
mFirst = testHelper.createRow();
mFirst.setHeadsUpAnimatingAwayListener(animatingAway
- -> mRoundnessManager.onHeadsupAnimatingAwayChanged(mFirst, animatingAway));
+ -> mRoundnessManager.updateView(mFirst, false));
mSecond = testHelper.createRow();
mSecond.setHeadsUpAnimatingAwayListener(animatingAway
- -> mRoundnessManager.onHeadsupAnimatingAwayChanged(mSecond, animatingAway));
+ -> mRoundnessManager.updateView(mSecond, false));
mRoundnessManager.setOnRoundingChangedCallback(mRoundnessCallback);
mRoundnessManager.setAnimatedChildren(mAnimatedChildren);
mRoundnessManager.updateRoundedChildren(new NotificationSection[]{
@@ -160,12 +160,12 @@
when(testHelper.getStatusBarStateController().isDozing()).thenReturn(true);
row.setHeadsUp(true);
- mRoundnessManager.onHeadsUpStateChanged(entry, true);
+ mRoundnessManager.updateView(entry.getRow(), false);
Assert.assertEquals(1f, row.getCurrentBottomRoundness(), 0.0f);
Assert.assertEquals(1f, row.getCurrentTopRoundness(), 0.0f);
row.setHeadsUp(false);
- mRoundnessManager.onHeadsUpStateChanged(entry, false);
+ mRoundnessManager.updateView(entry.getRow(), false);
Assert.assertEquals(0f, row.getCurrentBottomRoundness(), 0.0f);
Assert.assertEquals(0f, row.getCurrentTopRoundness(), 0.0f);
}
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 81cbef7..bb85cec 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
@@ -3,7 +3,10 @@
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 *
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -17,6 +20,7 @@
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;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
@@ -30,7 +34,6 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -43,8 +46,6 @@
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
@@ -55,18 +56,16 @@
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.FooterView;
-import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.KeyguardBypassEnabledProvider;
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;
import org.junit.Test;
@@ -90,14 +89,12 @@
@Rule public MockitoRule mockito = MockitoJUnit.rule();
@Mock private StatusBar mBar;
@Mock private SysuiStatusBarStateController mBarState;
- @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
@Mock private NotificationGroupManagerLegacy mGroupMembershipManger;
@Mock private NotificationGroupManagerLegacy mGroupExpansionManager;
@Mock private ExpandHelper mExpandHelper;
@Mock private EmptyShadeView mEmptyShadeView;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private RemoteInputController mRemoteInputController;
- @Mock private MetricsLogger mMetricsLogger;
@Mock private NotificationRoundnessManager mNotificationRoundnessManager;
@Mock private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider;
@Mock private NotificationSectionsManager mNotificationSectionsManager;
@@ -106,8 +103,6 @@
@Mock private NotificationSwipeHelper mNotificationSwipeHelper;
@Mock private NotificationStackScrollLayoutController mStackScrollLayoutController;
private int mOriginalInterruptionModelSetting;
- private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();
-
@Before
@UiThreadTest
@@ -122,16 +117,9 @@
1, UserHandle.USER_CURRENT);
// Inject dependencies before initializing the layout
- mDependency.injectMockDependency(VisualStabilityManager.class);
- mDependency.injectTestDependency(
- NotificationBlockingHelperManager.class,
- mBlockingHelperManager);
mDependency.injectTestDependency(SysuiStatusBarStateController.class, mBarState);
- mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
- mDependency.injectTestDependency(NotificationRemoteInputManager.class,
- mRemoteInputManager);
mDependency.injectMockDependency(ShadeController.class);
- when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
+
NotificationShelfController notificationShelfController =
mock(NotificationShelfController.class);
NotificationShelf notificationShelf = mock(NotificationShelf.class);
@@ -140,6 +128,8 @@
new NotificationSection[]{
mNotificationSection
});
+ when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
+
// The actual class under test. You may need to work with this class directly when
// testing anonymous class members of mStackScroller, like mMenuEventListener,
// which refer to members of NotificationStackScrollLayout. The spy
@@ -148,14 +138,10 @@
mStackScrollerInternal = new NotificationStackScrollLayout(
getContext(),
null,
- mNotificationRoundnessManager,
- mStatusBarStateController,
mNotificationSectionsManager,
- mock(ForegroundServiceSectionController.class),
- mock(ForegroundServiceDismissalFeatureController.class),
mGroupMembershipManger,
mGroupExpansionManager,
- mUiEventLoggerFake
+ mStatusBarStateController
);
mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider,
mNotificationSwipeHelper);
@@ -166,7 +152,8 @@
when(mStackScrollLayoutController.getNoticationRoundessManager())
.thenReturn(mNotificationRoundnessManager);
mStackScroller.setController(mStackScrollLayoutController);
-
+ mStackScroller.setRemoteInputManager(mRemoteInputManager);
+
// Stub out functionality that isn't necessary to test.
doNothing().when(mBar)
.executeRunnableDismissingKeyguard(any(Runnable.class),
@@ -226,12 +213,20 @@
@Test
@UiThreadTest
public void testSetExpandedHeight_blockingHelperManagerReceivedCallbacks() {
- mStackScroller.setExpandedHeight(0f);
- verify(mBlockingHelperManager).setNotificationShadeExpanded(0f);
- reset(mBlockingHelperManager);
+ final float expectedHeight[] = {0f};
+ final float expectedAppear[] = {0f};
- mStackScroller.setExpandedHeight(100f);
- verify(mBlockingHelperManager).setNotificationShadeExpanded(100f);
+ mStackScroller.addOnExpandedHeightChangedListener((height, appear) -> {
+ Assert.assertEquals(expectedHeight[0], height, 0);
+ Assert.assertEquals(expectedAppear[0], appear, .1);
+ });
+ expectedHeight[0] = 1f;
+ expectedAppear[0] = 1f;
+ mStackScroller.setExpandedHeight(expectedHeight[0]);
+
+ expectedHeight[0] = 100f;
+ expectedAppear[0] = 0f;
+ mStackScroller.setExpandedHeight(expectedHeight[0]);
}
@Test
@@ -380,18 +375,30 @@
@Test
public void testClearNotifications_All() {
+ final int[] numCalls = {0};
+ final int[] selected = {-1};
+ mStackScroller.setDismissListener(selectedRows -> {
+ numCalls[0]++;
+ selected[0] = selectedRows;
+ });
+
mStackScroller.clearNotifications(ROWS_ALL, true);
- assertEquals(1, mUiEventLoggerFake.numLogs());
- assertEquals(NotificationStackScrollLayout.NotificationPanelEvent
- .DISMISS_ALL_NOTIFICATIONS_PANEL.getId(), mUiEventLoggerFake.eventId(0));
+ assertEquals(1, numCalls[0]);
+ assertEquals(ROWS_ALL, selected[0]);
}
@Test
public void testClearNotifications_Gentle() {
+ final int[] numCalls = {0};
+ final int[] selected = {-1};
+ mStackScroller.setDismissListener(selectedRows -> {
+ numCalls[0]++;
+ selected[0] = selectedRows;
+ });
+
mStackScroller.clearNotifications(NotificationStackScrollLayout.ROWS_GENTLE, false);
- assertEquals(1, mUiEventLoggerFake.numLogs());
- assertEquals(NotificationStackScrollLayout.NotificationPanelEvent
- .DISMISS_SILENT_NOTIFICATIONS_PANEL.getId(), mUiEventLoggerFake.eventId(0));
+ assertEquals(1, numCalls[0]);
+ assertEquals(ROWS_GENTLE, selected[0]);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
index 60d6c53..01d49c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
@@ -18,6 +18,7 @@
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -32,10 +33,12 @@
import android.content.res.Resources;
import android.metrics.LogMaker;
import android.testing.AndroidTestingRunner;
+import android.view.LayoutInflater;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.SysuiTestCase;
@@ -48,18 +51,25 @@
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -97,7 +107,6 @@
@Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@Mock private MetricsLogger mMetricsLogger;
@Mock private FalsingManager mFalsingManager;
- @Mock private NotificationSectionsManager mNotificationSectionsManager;
@Mock private Resources mResources;
@Mock(answer = Answers.RETURNS_SELF)
private NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
@@ -111,6 +120,15 @@
@Mock private NotifCollection mNotifCollection;
@Mock private NotificationEntryManager mEntryManager;
@Mock private IStatusBarService mIStatusBarService;
+ @Mock private UiEventLogger mUiEventLogger;
+ @Mock private ForegroundServiceDismissalFeatureController mFgFeatureController;
+ @Mock private ForegroundServiceSectionController mFgServicesSectionController;
+ @Mock private ForegroundServiceDungeonView mForegroundServiceDungeonView;
+ @Mock private LayoutInflater mLayoutInflater;
+ @Mock private NotificationRemoteInputManager mRemoteInputManager;
+ @Mock private RemoteInputController mRemoteInputController;
+ @Mock private VisualStabilityManager mVisualStabilityManager;
+ @Mock private ShadeController mShadeController;
@Captor
private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
@@ -123,6 +141,9 @@
when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper);
when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
+ when(mFgServicesSectionController.createView(mLayoutInflater))
+ .thenReturn(mForegroundServiceDungeonView);
+ when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
mController = new NotificationStackScrollLayoutController(
true,
@@ -140,7 +161,6 @@
mNotificationLockscreenUserManager,
mMetricsLogger,
mFalsingManager,
- mNotificationSectionsManager,
mResources,
mNotificationSwipeHelperBuilder,
mStatusBar,
@@ -152,13 +172,19 @@
mNotifPipeline,
mNotifCollection,
mEntryManager,
- mIStatusBarService
+ mIStatusBarService,
+ mUiEventLogger,
+ mFgFeatureController,
+ mFgServicesSectionController,
+ mLayoutInflater,
+ mRemoteInputManager,
+ mVisualStabilityManager,
+ mShadeController
);
when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true);
}
-
@Test
public void testAttach_viewAlreadyAttached() {
mController.attach(mNotificationStackScrollLayout);
@@ -270,15 +296,12 @@
verify(mNotificationStackScrollLayout).updateSensitiveness(false, true);
}
-
@Test
- public void testOnMenuShownLogging() { ;
-
+ public void testOnMenuShownLogging() {
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker(
MetricsProto.MetricsEvent.VIEW_UNKNOWN));
-
ArgumentCaptor<OnMenuEventListener> onMenuEventListenerArgumentCaptor =
ArgumentCaptor.forClass(OnMenuEventListener.class);
@@ -300,7 +323,6 @@
when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker(
MetricsProto.MetricsEvent.VIEW_UNKNOWN));
-
ArgumentCaptor<OnMenuEventListener> onMenuEventListenerArgumentCaptor =
ArgumentCaptor.forClass(OnMenuEventListener.class);
@@ -317,6 +339,39 @@
MetricsProto.MetricsEvent.TYPE_ACTION));
}
+ @Test
+ public void testDismissListener() {
+ ArgumentCaptor<NotificationStackScrollLayout.DismissListener>
+ dismissListenerArgumentCaptor = ArgumentCaptor.forClass(
+ NotificationStackScrollLayout.DismissListener.class);
+
+ mController.attach(mNotificationStackScrollLayout);
+
+ verify(mNotificationStackScrollLayout).setDismissListener(
+ dismissListenerArgumentCaptor.capture());
+ NotificationStackScrollLayout.DismissListener dismissListener =
+ dismissListenerArgumentCaptor.getValue();
+
+ dismissListener.onDismiss(ROWS_ALL);
+ verify(mUiEventLogger).log(NotificationPanelEvent.fromSelection(ROWS_ALL));
+ }
+
+ @Test
+ public void testForegroundDismissEnabled() {
+ when(mFgFeatureController.isForegroundServiceDismissalEnabled()).thenReturn(true);
+ mController.attach(mNotificationStackScrollLayout);
+ verify(mNotificationStackScrollLayout).initializeForegroundServiceSection(
+ mForegroundServiceDungeonView);
+ }
+
+ @Test
+ public void testForegroundDismissaDisabled() {
+ when(mFgFeatureController.isForegroundServiceDismissalEnabled()).thenReturn(false);
+ mController.attach(mNotificationStackScrollLayout);
+ verify(mNotificationStackScrollLayout, never()).initializeForegroundServiceSection(
+ any(ForegroundServiceDungeonView.class));
+ }
+
private LogMaker logMatcher(int category, int type) {
return argThat(new LogMatcher(category, type));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 37ccac0..23c0930 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -179,7 +179,8 @@
HashSet<Integer> reasonsThatDontPulse = new HashSet<>(
Arrays.asList(DozeLog.REASON_SENSOR_PICKUP,
DozeLog.REASON_SENSOR_DOUBLE_TAP,
- DozeLog.REASON_SENSOR_TAP));
+ DozeLog.REASON_SENSOR_TAP,
+ DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS));
doAnswer(invocation -> {
DozeHost.PulseCallback callback = invocation.getArgument(0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
index 85b5d70..06d0331 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
@@ -18,11 +18,13 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.res.Resources;
import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -48,7 +50,6 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class LockscreenIconControllerTest extends SysuiTestCase {
- private LockscreenLockIconController mLockIconController;
@Mock
private LockscreenGestureLogger mLockscreenGestureLogger;
@Mock
@@ -80,11 +81,15 @@
@Mock
private HeadsUpManagerPhone mHeadsUpManagerPhone;
+ private LockscreenLockIconController mLockIconController;
+ private OnAttachStateChangeListener mOnAttachStateChangeListener;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ when(mLockIcon.getContext()).thenReturn(mContext);
mLockIconController = new LockscreenLockIconController(
mLockscreenGestureLogger, mKeyguardUpdateMonitor, mLockPatternUtils,
mShadeController, mAccessibilityController, mKeyguardIndicationController,
@@ -92,7 +97,15 @@
mKeyguardBypassController, mDockManager, mKeyguardStateController, mResources,
mHeadsUpManagerPhone);
+ ArgumentCaptor<OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor =
+ ArgumentCaptor.forClass(OnAttachStateChangeListener.class);
+
+ doNothing().when(mLockIcon)
+ .addOnAttachStateChangeListener(
+ onAttachStateChangeListenerArgumentCaptor.capture());
mLockIconController.attach(mLockIcon);
+
+ mOnAttachStateChangeListener = onAttachStateChangeListenerArgumentCaptor.getValue();
}
@Test
@@ -114,4 +127,19 @@
verify(mLockPatternUtils).requireCredentialEntry(anyInt());
verify(mKeyguardUpdateMonitor).onLockIconPressed();
}
+
+ @Test
+ public void testVisibility_Dozing() {
+ ArgumentCaptor<StatusBarStateController.StateListener> sBStateListenerCaptor =
+ ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
+
+ mOnAttachStateChangeListener.onViewAttachedToWindow(mLockIcon);
+ verify(mStatusBarStateController).addCallback(sBStateListenerCaptor.capture());
+
+ when(mStatusBarStateController.isDozing()).thenReturn(true);
+ sBStateListenerCaptor.getValue().onDozingChanged(true);
+
+ verify(mLockIcon).updateIconVisibility(false);
+
+ }
}
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 94d5458..ef4d107 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.wmshell;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -88,7 +89,8 @@
public void initPip_registersCommandQueueCallback() {
mWMShell.initPip(mPip);
- verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class));
+ // Once for the shell, once for pip
+ verify(mCommandQueue, times(2)).addCallback(any(CommandQueue.Callbacks.class));
}
@Test
@@ -106,7 +108,8 @@
mWMShell.initOneHanded(mOneHanded);
verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
- verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class));
+ // Once for the shell, once for the one handed mode
+ verify(mCommandQueue, times(2)).addCallback(any(CommandQueue.Callbacks.class));
verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer.class));
verify(mNavigationModeController).addListener(
any(NavigationModeController.ModeChangedListener.class));
diff --git a/packages/Tethering/apex/Android.bp b/packages/Tethering/apex/Android.bp
index 67097a7..0524374 100644
--- a/packages/Tethering/apex/Android.bp
+++ b/packages/Tethering/apex/Android.bp
@@ -19,6 +19,7 @@
updatable: true,
min_sdk_version: "current",
java_libs: ["framework-tethering"],
+ bpfs: ["offload.o"],
apps: ["Tethering"],
manifest: "manifest.json",
key: "com.android.tethering.key",
diff --git a/packages/Tethering/bpf_progs/Android.bp b/packages/Tethering/bpf_progs/Android.bp
new file mode 100644
index 0000000..d54f861
--- /dev/null
+++ b/packages/Tethering/bpf_progs/Android.bp
@@ -0,0 +1,33 @@
+//
+// 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.
+//
+
+//
+// bpf kernel programs
+//
+bpf {
+ name: "offload.o",
+ srcs: ["offload.c"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ include_dirs: [
+ // TODO: get rid of system/netd.
+ "system/netd/bpf_progs", // for bpf_net_helpers.h
+ "system/netd/libnetdbpf/include", // for bpf_shared.h
+ "system/netd/libnetdutils/include", // for UidConstants.h
+ ],
+}
diff --git a/packages/Tethering/bpf_progs/offload.c b/packages/Tethering/bpf_progs/offload.c
new file mode 100644
index 0000000..cc5af31
--- /dev/null
+++ b/packages/Tethering/bpf_progs/offload.c
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ */
+
+#include <linux/if.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/pkt_cls.h>
+#include <linux/tcp.h>
+
+#include "bpf_helpers.h"
+#include "bpf_net_helpers.h"
+#include "netdbpf/bpf_shared.h"
+
+DEFINE_BPF_MAP_GRW(tether_ingress_map, HASH, TetherIngressKey, TetherIngressValue, 64,
+ AID_NETWORK_STACK)
+
+// Tethering stats, indexed by upstream interface.
+DEFINE_BPF_MAP_GRW(tether_stats_map, HASH, uint32_t, TetherStatsValue, 16, AID_NETWORK_STACK)
+
+// Tethering data limit, indexed by upstream interface.
+// (tethering allowed when stats[iif].rxBytes + stats[iif].txBytes < limit[iif])
+DEFINE_BPF_MAP_GRW(tether_limit_map, HASH, uint32_t, uint64_t, 16, AID_NETWORK_STACK)
+
+static inline __always_inline int do_forward(struct __sk_buff* skb, bool is_ethernet) {
+ int l2_header_size = is_ethernet ? sizeof(struct ethhdr) : 0;
+ void* data = (void*)(long)skb->data;
+ const void* data_end = (void*)(long)skb->data_end;
+ struct ethhdr* eth = is_ethernet ? data : NULL; // used iff is_ethernet
+ struct ipv6hdr* ip6 = is_ethernet ? (void*)(eth + 1) : data;
+
+ // Must be meta-ethernet IPv6 frame
+ if (skb->protocol != htons(ETH_P_IPV6)) return TC_ACT_OK;
+
+ // Must have (ethernet and) ipv6 header
+ if (data + l2_header_size + sizeof(*ip6) > data_end) return TC_ACT_OK;
+
+ // Ethertype - if present - must be IPv6
+ if (is_ethernet && (eth->h_proto != htons(ETH_P_IPV6))) return TC_ACT_OK;
+
+ // IP version must be 6
+ if (ip6->version != 6) return TC_ACT_OK;
+
+ // Cannot decrement during forward if already zero or would be zero,
+ // Let the kernel's stack handle these cases and generate appropriate ICMP errors.
+ if (ip6->hop_limit <= 1) return TC_ACT_OK;
+
+ // Protect against forwarding packets sourced from ::1 or fe80::/64 or other weirdness.
+ __be32 src32 = ip6->saddr.s6_addr32[0];
+ if (src32 != htonl(0x0064ff9b) && // 64:ff9b:/32 incl. XLAT464 WKP
+ (src32 & htonl(0xe0000000)) != htonl(0x20000000)) // 2000::/3 Global Unicast
+ return TC_ACT_OK;
+
+ TetherIngressKey k = {
+ .iif = skb->ifindex,
+ .neigh6 = ip6->daddr,
+ };
+
+ TetherIngressValue* v = bpf_tether_ingress_map_lookup_elem(&k);
+
+ // If we don't find any offload information then simply let the core stack handle it...
+ if (!v) return TC_ACT_OK;
+
+ uint32_t stat_and_limit_k = skb->ifindex;
+
+ TetherStatsValue* stat_v = bpf_tether_stats_map_lookup_elem(&stat_and_limit_k);
+
+ // If we don't have anywhere to put stats, then abort...
+ if (!stat_v) return TC_ACT_OK;
+
+ uint64_t* limit_v = bpf_tether_limit_map_lookup_elem(&stat_and_limit_k);
+
+ // If we don't have a limit, then abort...
+ if (!limit_v) return TC_ACT_OK;
+
+ // Required IPv6 minimum mtu is 1280, below that not clear what we should do, abort...
+ const int pmtu = v->pmtu;
+ if (pmtu < IPV6_MIN_MTU) return TC_ACT_OK;
+
+ // Approximate handling of TCP/IPv6 overhead for incoming LRO/GRO packets: default
+ // outbound path mtu of 1500 is not necessarily correct, but worst case we simply
+ // undercount, which is still better then not accounting for this overhead at all.
+ // Note: this really shouldn't be device/path mtu at all, but rather should be
+ // derived from this particular connection's mss (ie. from gro segment size).
+ // This would require a much newer kernel with newer ebpf accessors.
+ // (This is also blindly assuming 12 bytes of tcp timestamp option in tcp header)
+ uint64_t packets = 1;
+ uint64_t bytes = skb->len;
+ if (bytes > pmtu) {
+ const int tcp_overhead = sizeof(struct ipv6hdr) + sizeof(struct tcphdr) + 12;
+ const int mss = pmtu - tcp_overhead;
+ const uint64_t payload = bytes - tcp_overhead;
+ packets = (payload + mss - 1) / mss;
+ bytes = tcp_overhead * packets + payload;
+ }
+
+ // Are we past the limit? If so, then abort...
+ // Note: will not overflow since u64 is 936 years even at 5Gbps.
+ // Do not drop here. Offload is just that, whenever we fail to handle
+ // a packet we let the core stack deal with things.
+ // (The core stack needs to handle limits correctly anyway,
+ // since we don't offload all traffic in both directions)
+ if (stat_v->rxBytes + stat_v->txBytes + bytes > *limit_v) return TC_ACT_OK;
+
+ if (!is_ethernet) {
+ is_ethernet = true;
+ l2_header_size = sizeof(struct ethhdr);
+ // Try to inject an ethernet header, and simply return if we fail
+ if (bpf_skb_change_head(skb, l2_header_size, /*flags*/ 0)) {
+ __sync_fetch_and_add(&stat_v->rxErrors, 1);
+ return TC_ACT_OK;
+ }
+
+ // bpf_skb_change_head() invalidates all pointers - reload them
+ data = (void*)(long)skb->data;
+ data_end = (void*)(long)skb->data_end;
+ eth = data;
+ ip6 = (void*)(eth + 1);
+
+ // I do not believe this can ever happen, but keep the verifier happy...
+ if (data + l2_header_size + sizeof(*ip6) > data_end) {
+ __sync_fetch_and_add(&stat_v->rxErrors, 1);
+ return TC_ACT_SHOT;
+ }
+ };
+
+ // CHECKSUM_COMPLETE is a 16-bit one's complement sum,
+ // thus corrections for it need to be done in 16-byte chunks at even offsets.
+ // IPv6 nexthdr is at offset 6, while hop limit is at offset 7
+ uint8_t old_hl = ip6->hop_limit;
+ --ip6->hop_limit;
+ uint8_t new_hl = ip6->hop_limit;
+
+ // bpf_csum_update() always succeeds if the skb is CHECKSUM_COMPLETE and returns an error
+ // (-ENOTSUPP) if it isn't.
+ bpf_csum_update(skb, 0xFFFF - ntohs(old_hl) + ntohs(new_hl));
+
+ __sync_fetch_and_add(&stat_v->rxPackets, packets);
+ __sync_fetch_and_add(&stat_v->rxBytes, bytes);
+
+ // Overwrite any mac header with the new one
+ *eth = v->macHeader;
+
+ // Redirect to forwarded interface.
+ //
+ // Note that bpf_redirect() cannot fail unless you pass invalid flags.
+ // The redirect actually happens after the ebpf program has already terminated,
+ // and can fail for example for mtu reasons at that point in time, but there's nothing
+ // we can do about it here.
+ return bpf_redirect(v->oif, 0 /* this is effectively BPF_F_EGRESS */);
+}
+
+SEC("schedcls/ingress/tether_ether")
+int sched_cls_ingress_tether_ether(struct __sk_buff* skb) {
+ return do_forward(skb, true);
+}
+
+// Note: section names must be unique to prevent programs from appending to each other,
+// so instead the bpf loader will strip everything past the final $ symbol when actually
+// pinning the program into the filesystem.
+//
+// bpf_skb_change_head() is only present on 4.14+ and 2 trivial kernel patches are needed:
+// ANDROID: net: bpf: Allow TC programs to call BPF_FUNC_skb_change_head
+// ANDROID: net: bpf: permit redirect from ingress L3 to egress L2 devices at near max mtu
+// (the first of those has already been upstreamed)
+//
+// 5.4 kernel support was only added to Android Common Kernel in R,
+// and thus a 5.4 kernel always supports this.
+//
+// Hence, this mandatory (must load successfully) implementation for 5.4+ kernels:
+DEFINE_BPF_PROG_KVER("schedcls/ingress/tether_rawip$5_4", AID_ROOT, AID_ROOT,
+ sched_cls_ingress_tether_rawip_5_4, KVER(5, 4, 0))
+(struct __sk_buff* skb) {
+ return do_forward(skb, false);
+}
+
+// and this identical optional (may fail to load) implementation for [4.14..5.4) patched kernels:
+DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/ingress/tether_rawip$4_14", AID_ROOT, AID_ROOT,
+ sched_cls_ingress_tether_rawip_4_14, KVER(4, 14, 0),
+ KVER(5, 4, 0))
+(struct __sk_buff* skb) {
+ return do_forward(skb, false);
+}
+
+// and define a no-op stub for [4.9,4.14) and unpatched [4.14,5.4) kernels.
+// (if the above real 4.14+ program loaded successfully, then bpfloader will have already pinned
+// it at the same location this one would be pinned at and will thus skip loading this stub)
+DEFINE_BPF_PROG_KVER_RANGE("schedcls/ingress/tether_rawip$stub", AID_ROOT, AID_ROOT,
+ sched_cls_ingress_tether_rawip_stub, KVER_NONE, KVER(5, 4, 0))
+(struct __sk_buff* skb) {
+ return TC_ACT_OK;
+}
+
+LICENSE("Apache 2.0");
+CRITICAL("netd");
diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml b/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml
index 96ed7b4..06cb33b 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml
@@ -31,7 +31,7 @@
<bool name="config_navBarTapThrough">true</bool>
<!-- Controls the size of the back gesture inset. -->
- <dimen name="config_backGestureInset">24dp</dimen>
+ <dimen name="config_backGestureInset">30dp</dimen>
<!-- Controls whether the navbar needs a scrim with
{@link Window#setEnsuringNavigationBarContrastWhenTransparent}. -->
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index c41f400..80e9703 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -120,6 +120,7 @@
import com.android.server.SystemService;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
import com.android.server.accessibility.magnification.MagnificationGestureHandler;
+import com.android.server.accessibility.magnification.MagnificationTransitionController;
import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -258,6 +259,7 @@
private Point mTempPoint = new Point();
private boolean mIsAccessibilityButtonShown;
+ private MagnificationTransitionController mMagnificationTransitionController;
private AccessibilityUserState getCurrentUserStateLocked() {
return getUserStateLocked(mCurrentUserId);
@@ -302,6 +304,7 @@
mA11yWindowManager = a11yWindowManager;
mA11yDisplayListener = a11yDisplayListener;
mWindowMagnificationMgr = windowMagnificationMgr;
+ mMagnificationTransitionController = new MagnificationTransitionController(this, mLock);
init();
}
@@ -321,6 +324,7 @@
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
mWindowManagerService, this, mSecurityPolicy, this);
mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
+ mMagnificationTransitionController = new MagnificationTransitionController(this, mLock);
init();
}
@@ -1550,9 +1554,26 @@
if (fallBackMagnificationModeSettingsLocked(userState)) {
return;
}
- mMainHandler.sendMessage(obtainMessage(
- AccessibilityManagerService::notifyRefreshMagnificationModeToInputFilter,
- this));
+ mMagnificationTransitionController.transitionMagnificationModeLocked(
+ Display.DEFAULT_DISPLAY, userState.getMagnificationModeLocked(),
+ this::onMagnificationTransitionEndedLocked);
+ }
+
+ /**
+ * Called when the magnification mode transition is completed.
+ */
+ void onMagnificationTransitionEndedLocked(boolean success) {
+ final AccessibilityUserState userState = getCurrentUserStateLocked();
+ final int previousMode = userState.getMagnificationModeLocked()
+ ^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
+ if (!success && previousMode != 0) {
+ userState.setMagnificationModeLocked(previousMode);
+ persistMagnificationModeSettingLocked(previousMode);
+ } else {
+ mMainHandler.sendMessage(obtainMessage(
+ AccessibilityManagerService::notifyRefreshMagnificationModeToInputFilter,
+ this));
+ }
}
private void notifyRefreshMagnificationModeToInputFilter() {
@@ -2962,7 +2983,12 @@
getWindowMagnificationMgr().setConnection(connection);
}
- WindowMagnificationManager getWindowMagnificationMgr() {
+ /**
+ * Getter of {@link WindowMagnificationManager}.
+ *
+ * @return WindowMagnificationManager
+ */
+ public WindowMagnificationManager getWindowMagnificationMgr() {
synchronized (mLock) {
if (mWindowMagnificationMgr == null) {
mWindowMagnificationMgr = new WindowMagnificationManager(mContext, mCurrentUserId);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index a75b64c..ee03b15 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -1352,7 +1352,7 @@
mTouchInteractionInProgress = false;
// We want to set the active window to be current immediately
// after the user has stopped touching the screen since if the
- // user types with the IME he should get a feedback for the
+ // user types with the IME they should get a feedback for the
// letter typed in the text view which is in the input focused
// window. Note that we always deliver hover accessibility events
// (they are a result of user touching the screen) so change of
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
index 8604fe7..a860db3 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -207,7 +207,7 @@
boolean onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
if (mState.isClear()) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- // Sanity safeguard: if touch state is clear, then matchers should always be clear
+ // Validity safeguard: if touch state is clear, then matchers should always be clear
// before processing the next down event.
clear();
} else {
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 8305be3..d8c692b8 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -720,7 +720,7 @@
}
// If the user is touch exploring the second pointer may be
// performing a double tap to activate an item without need
- // for the user to lift his exploring finger.
+ // for the user to lift their exploring finger.
// It is *important* to use the distance traveled by the pointers
// on the screen which may or may not be magnified.
final float deltaX =
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java
new file mode 100644
index 0000000..af4b34f
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java
@@ -0,0 +1,237 @@
+/*
+ * 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.accessibility.magnification;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.provider.Settings;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.accessibility.MagnificationAnimationCallback;
+
+import com.android.server.accessibility.AccessibilityManagerService;
+
+/**
+ * Handles magnification mode transition.
+ */
+public class MagnificationTransitionController {
+
+ private static final boolean DEBUG = false;
+ private static final String TAG = "MagnificationController";
+ private final AccessibilityManagerService mAms;
+ private final PointF mTempPoint = new PointF();
+ private final Object mLock;
+ private final SparseArray<DisableMagnificationCallback>
+ mMagnificationEndRunnableSparseArray = new SparseArray();
+
+
+ /**
+ * A callback to inform the magnification transition result.
+ */
+ public interface TransitionCallBack {
+ /**
+ * Invoked when the transition ends.
+ * @param success {@code true} if the transition success.
+ */
+ void onResult(boolean success);
+ }
+
+ public MagnificationTransitionController(AccessibilityManagerService ams, Object lock) {
+ mAms = ams;
+ mLock = lock;
+ }
+
+ /**
+ * Transitions to the target Magnification mode with current center of the magnification mode
+ * if it is available.
+ *
+ * @param displayId The logical display
+ * @param targetMode The target magnification mode
+ * @param transitionCallBack The callback invoked when the transition is finished.
+ */
+ public void transitionMagnificationModeLocked(int displayId, int targetMode,
+ @NonNull TransitionCallBack transitionCallBack) {
+ final PointF magnificationCenter = getCurrentMagnificationBoundsCenterLocked(displayId,
+ targetMode);
+ final DisableMagnificationCallback animationCallback =
+ getDisableMagnificationEndRunnableLocked(displayId);
+ if (magnificationCenter == null && animationCallback == null) {
+ transitionCallBack.onResult(true);
+ return;
+ }
+
+ if (animationCallback != null) {
+ if (animationCallback.mCurrentMode == targetMode) {
+ animationCallback.restoreToCurrentMagnificationMode();
+ return;
+ }
+ Slog.w(TAG, "request during transition, abandon current:"
+ + animationCallback.mTargetMode);
+ animationCallback.setExpiredAndRemoveFromListLocked();
+ }
+
+ if (magnificationCenter == null) {
+ Slog.w(TAG, "Invalid center, ignore it");
+ transitionCallBack.onResult(true);
+ return;
+ }
+ final FullScreenMagnificationController screenMagnificationController =
+ getFullScreenMagnificationController();
+ final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationManager();
+ final float scale = windowMagnificationMgr.getPersistedScale();
+ final DisableMagnificationCallback animationEndCallback =
+ new DisableMagnificationCallback(transitionCallBack, displayId, targetMode,
+ scale, magnificationCenter);
+ if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
+ screenMagnificationController.reset(displayId, animationEndCallback);
+ } else {
+ windowMagnificationMgr.disableWindowMagnification(displayId, false,
+ animationEndCallback);
+ }
+ setDisableMagnificationCallbackLocked(displayId, animationEndCallback);
+ }
+
+ private DisableMagnificationCallback getDisableMagnificationEndRunnableLocked(
+ int displayId) {
+ return mMagnificationEndRunnableSparseArray.get(displayId);
+ }
+
+ private void setDisableMagnificationCallbackLocked(int displayId,
+ @Nullable DisableMagnificationCallback callback) {
+ mMagnificationEndRunnableSparseArray.put(displayId, callback);
+ if (DEBUG) {
+ Slog.d(TAG, "setDisableMagnificationCallbackLocked displayId = " + displayId
+ + ", callback = " + callback);
+ }
+ }
+
+ private FullScreenMagnificationController getFullScreenMagnificationController() {
+ return mAms.getFullScreenMagnificationController();
+ }
+
+ private WindowMagnificationManager getWindowMagnificationManager() {
+ return mAms.getWindowMagnificationMgr();
+ }
+
+ private @Nullable
+ PointF getCurrentMagnificationBoundsCenterLocked(int displayId, int targetMode) {
+ if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
+ final WindowMagnificationManager magnificationManager = getWindowMagnificationManager();
+ if (!magnificationManager.isWindowMagnifierEnabled(displayId)) {
+ return null;
+ }
+ mTempPoint.set(magnificationManager.getCenterX(displayId),
+ magnificationManager.getCenterY(displayId));
+ } else {
+ final FullScreenMagnificationController screenMagnificationController =
+ getFullScreenMagnificationController();
+ if (!screenMagnificationController.isMagnifying(displayId)) {
+ return null;
+ }
+ mTempPoint.set(screenMagnificationController.getCenterX(displayId),
+ screenMagnificationController.getCenterY(displayId));
+ }
+ return mTempPoint;
+ }
+
+ private final class DisableMagnificationCallback implements
+ MagnificationAnimationCallback {
+ private final TransitionCallBack mTransitionCallBack;
+ private boolean mExpired = false;
+ private final int mDisplayId;
+ private final int mTargetMode;
+ private final int mCurrentMode;
+ private final float mCurrentScale;
+ private final PointF mCurrentCenter = new PointF();
+
+ DisableMagnificationCallback(TransitionCallBack transitionCallBack,
+ int displayId, int targetMode, float scale, PointF currentCenter) {
+ mTransitionCallBack = transitionCallBack;
+ mDisplayId = displayId;
+ mTargetMode = targetMode;
+ mCurrentMode = mTargetMode ^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
+ mCurrentScale = scale;
+ mCurrentCenter.set(currentCenter);
+ }
+
+ @Override
+ public void onResult(boolean success) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onResult success = " + success);
+ }
+ if (mExpired) {
+ return;
+ }
+ setExpiredAndRemoveFromListLocked();
+ if (success) {
+ adjustCurrentCenterIfNeededLocked();
+ applyMagnificationModeLocked(mTargetMode);
+ }
+ mTransitionCallBack.onResult(success);
+ }
+ }
+
+ private void adjustCurrentCenterIfNeededLocked() {
+ if (mTargetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
+ return;
+ }
+ final Region outRegion = new Region();
+ getFullScreenMagnificationController().getMagnificationRegion(mDisplayId, outRegion);
+ if (outRegion.contains((int) mCurrentCenter.x, (int) mCurrentCenter.y)) {
+ return;
+ }
+ final Rect bounds = outRegion.getBounds();
+ mCurrentCenter.set(bounds.exactCenterX(), bounds.exactCenterY());
+ }
+
+ void restoreToCurrentMagnificationMode() {
+ synchronized (mLock) {
+ if (mExpired) {
+ return;
+ }
+ setExpiredAndRemoveFromListLocked();
+ applyMagnificationModeLocked(mCurrentMode);
+ mTransitionCallBack.onResult(true);
+ }
+ }
+
+ void setExpiredAndRemoveFromListLocked() {
+ mExpired = true;
+ setDisableMagnificationCallbackLocked(mDisplayId, null);
+ }
+
+ private void applyMagnificationModeLocked(int mode) {
+ if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
+ getFullScreenMagnificationController().setScaleAndCenter(mDisplayId,
+ mCurrentScale, mCurrentCenter.x,
+ mCurrentCenter.y, true,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ } else {
+ getWindowMagnificationManager().enableWindowMagnification(mDisplayId,
+ mCurrentScale, mCurrentCenter.x,
+ mCurrentCenter.y);
+ }
+ }
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
index a401bcd3..993027d 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
@@ -20,11 +20,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Slog;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
+import android.view.accessibility.MagnificationAnimationCallback;
/**
* A wrapper of {@link IWindowMagnificationConnection}.
@@ -50,9 +51,10 @@
}
boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
- @Nullable RemoteCallback endCallback) {
+ @Nullable MagnificationAnimationCallback callback) {
try {
- mConnection.enableWindowMagnification(displayId, scale, centerX, centerY, endCallback);
+ mConnection.enableWindowMagnification(displayId, scale, centerX, centerY,
+ transformToRemoteCallback(callback));
} catch (RemoteException e) {
if (DBG) {
Slog.e(TAG, "Error calling enableWindowMagnification()", e);
@@ -74,9 +76,10 @@
return true;
}
- boolean disableWindowMagnification(int displayId, @Nullable RemoteCallback endCallback) {
+ boolean disableWindowMagnification(int displayId,
+ @Nullable MagnificationAnimationCallback callback) {
try {
- mConnection.disableWindowMagnification(displayId, endCallback);
+ mConnection.disableWindowMagnification(displayId, transformToRemoteCallback(callback));
} catch (RemoteException e) {
if (DBG) {
Slog.e(TAG, "Error calling disableWindowMagnification()", e);
@@ -134,4 +137,27 @@
return true;
}
+ private static @Nullable
+ IRemoteMagnificationAnimationCallback transformToRemoteCallback(
+ MagnificationAnimationCallback callback) {
+ if (callback == null) {
+ return null;
+ }
+ return new RemoteAnimationCallback(callback);
+ }
+
+ private static class RemoteAnimationCallback extends
+ IRemoteMagnificationAnimationCallback.Stub {
+
+ private final MagnificationAnimationCallback mCallback;
+
+ RemoteAnimationCallback(@NonNull MagnificationAnimationCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void onResult(boolean success) throws RemoteException {
+ mCallback.onResult(success);
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index 9cbd78b..c8e485f 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -24,7 +24,6 @@
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
-import android.os.RemoteCallback;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.MathUtils;
@@ -33,6 +32,7 @@
import android.view.MotionEvent;
import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
+import android.view.accessibility.MagnificationAnimationCallback;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -91,7 +91,7 @@
*/
public void setConnection(@Nullable IWindowMagnificationConnection connection) {
synchronized (mLock) {
- //Reset connectionWrapper.
+ // Reset connectionWrapper.
if (mConnectionWrapper != null) {
mConnectionWrapper.setConnectionCallback(null);
if (mConnectionCallback != null) {
@@ -241,11 +241,10 @@
* or {@link Float#NaN} to leave unchanged.
* @param centerY The screen-relative Y coordinate around which to center,
* or {@link Float#NaN} to leave unchanged.
- * @param endCallback Called when the animation is ended without any interruption or the
- * window magnifier is disabled already.
+ * @param animationCallback Called when the animation result is valid.
*/
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
- @Nullable Runnable endCallback) {
+ @Nullable MagnificationAnimationCallback animationCallback) {
synchronized (mLock) {
if (mConnectionWrapper == null) {
return;
@@ -254,7 +253,8 @@
if (magnifier == null) {
magnifier = createWindowMagnifier(displayId);
}
- magnifier.enableWindowMagnificationInternal(scale, centerX, centerY, endCallback);
+ magnifier.enableWindowMagnificationInternal(scale, centerX, centerY,
+ animationCallback);
}
}
@@ -273,16 +273,16 @@
*
* @param displayId The logical display id.
* @param clear {@true} Clears the state of window magnification.
- * @param endCallback Called when the animation is ended without any interruption or the
- * window magnifier is disabled already.
+ * @param animationCallback Called when the animation result is valid.
*/
- void disableWindowMagnification(int displayId, boolean clear, Runnable endCallback) {
+ void disableWindowMagnification(int displayId, boolean clear,
+ MagnificationAnimationCallback animationCallback) {
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null || mConnectionWrapper == null) {
return;
}
- magnifier.disableWindowMagnificationInternal(endCallback);
+ magnifier.disableWindowMagnificationInternal(animationCallback);
if (clear) {
mWindowMagnifiers.delete(displayId);
}
@@ -312,7 +312,8 @@
* @param displayId The logical display id.
* @return {@code true} if the window magnification is enabled.
*/
- boolean isWindowMagnifierEnabled(int displayId) {
+ @VisibleForTesting
+ public boolean isWindowMagnifierEnabled(int displayId) {
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null) {
@@ -408,6 +409,38 @@
}
/**
+ * Returns the screen-relative X coordinate of the center of the magnified bounds.
+ *
+ * @param displayId The logical display id
+ * @return the X coordinate. {@link Float#NaN} if the window magnification is not enabled.
+ */
+ float getCenterX(int displayId) {
+ synchronized (mLock) {
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ return Float.NaN;
+ }
+ return magnifier.getCenterX();
+ }
+ }
+
+ /**
+ * Returns the screen-relative Y coordinate of the center of the magnified bounds.
+ *
+ * @param displayId The logical display id
+ * @return the Y coordinate. {@link Float#NaN} if the window magnification is not enabled.
+ */
+ float getCenterY(int displayId) {
+ synchronized (mLock) {
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ return Float.NaN;
+ }
+ return magnifier.getCenterY();
+ }
+ }
+
+ /**
* Creates the windowMagnifier based on the specified display and stores it.
*
* @param displayId logical display id.
@@ -446,11 +479,13 @@
@Override
public void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
- WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
- if (magnifier == null) {
- magnifier = createWindowMagnifier(displayId);
+ synchronized (mLock) {
+ WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier == null) {
+ magnifier = createWindowMagnifier(displayId);
+ }
+ magnifier.onSourceBoundsChanged(sourceBounds);
}
- magnifier.onSourceBoundsChanged(sourceBounds);
}
@Override
@@ -479,9 +514,9 @@
private boolean mEnabled;
private final WindowMagnificationManager mWindowMagnificationManager;
- //Records the bounds of window magnification.
+ // Records the bounds of window magnification.
private final Rect mBounds = new Rect();
- //The magnified bounds on the screen.
+ // The magnified bounds on the screen.
private final Rect mSourceBounds = new Rect();
WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager) {
@@ -491,22 +526,23 @@
@GuardedBy("mLock")
void enableWindowMagnificationInternal(float scale, float centerX, float centerY,
- @Nullable Runnable endCallback) {
+ @Nullable MagnificationAnimationCallback animationCallback) {
if (mEnabled) {
return;
}
final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale,
- centerX, centerY, endCallback)) {
+ centerX, centerY, animationCallback)) {
mScale = normScale;
mEnabled = true;
}
}
@GuardedBy("mLock")
- void disableWindowMagnificationInternal(@Nullable Runnable endCallback) {
+ void disableWindowMagnificationInternal(
+ @Nullable MagnificationAnimationCallback animationResultCallback) {
if (mEnabled && mWindowMagnificationManager.disableWindowMagnificationInternal(
- mDisplayId, endCallback)) {
+ mDisplayId, animationResultCallback)) {
mEnabled = false;
}
}
@@ -562,26 +598,36 @@
mEnabled = false;
}
+ @GuardedBy("mLock")
public void onSourceBoundsChanged(Rect sourceBounds) {
mSourceBounds.set(sourceBounds);
}
+
+ @GuardedBy("mLock")
+ float getCenterX() {
+ return mEnabled ? mSourceBounds.exactCenterX() : Float.NaN;
+ }
+
+ @GuardedBy("mLock")
+ float getCenterY() {
+ return mEnabled ? mSourceBounds.exactCenterY() : Float.NaN;
+ }
}
private boolean enableWindowMagnificationInternal(int displayId, float scale, float centerX,
- float centerY, Runnable endCallback) {
+ float centerY, MagnificationAnimationCallback animationCallback) {
return mConnectionWrapper != null && mConnectionWrapper.enableWindowMagnification(
- displayId, scale, centerX, centerY,
- endCallback != null ? new RemoteCallback(bundle -> endCallback.run()) : null);
+ displayId, scale, centerX, centerY, animationCallback);
}
private boolean setScaleInternal(int displayId, float scale) {
return mConnectionWrapper != null && mConnectionWrapper.setScale(displayId, scale);
}
- private boolean disableWindowMagnificationInternal(int displayId, Runnable endCallback) {
+ private boolean disableWindowMagnificationInternal(int displayId,
+ MagnificationAnimationCallback animationCallback) {
return mConnectionWrapper != null && mConnectionWrapper.disableWindowMagnification(
- displayId,
- endCallback != null ? new RemoteCallback(bundle -> endCallback.run()) : null);
+ displayId, animationCallback);
}
private boolean moveWindowMagnifierInternal(int displayId, float offsetX, float offsetY) {
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index d7a3a32..060d097 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1001,7 +1001,7 @@
Slog.i(TAG, "hasBindAppWidgetPermission() " + UserHandle.getCallingUserId());
}
- // A special permission is required for managing white listing.
+ // A special permission is required for managing allowlisting.
mSecurityPolicy.enforceModifyAppWidgetBindPermissions(packageName);
synchronized (mLock) {
@@ -1025,7 +1025,7 @@
Slog.i(TAG, "setBindAppWidgetPermission() " + UserHandle.getCallingUserId());
}
- // A special permission is required for managing white listing.
+ // A special permission is required for managing allowlisting.
mSecurityPolicy.enforceModifyAppWidgetBindPermissions(packageName);
synchronized (mLock) {
@@ -1117,7 +1117,7 @@
}
// If the provider is not under the calling user, make sure this
- // provider is white listed for access from the parent.
+ // provider is allowlisted for access from the parent.
if (!mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed(
providerComponent.getPackageName(), providerProfileId)) {
return false;
@@ -1126,7 +1126,7 @@
synchronized (mLock) {
ensureGroupStateLoadedLocked(userId);
- // A special permission or white listing is required to bind widgets.
+ // A special permission or allowlisting is required to bind widgets.
if (!mSecurityPolicy.hasCallerBindPermissionOrBindWhiteListedLocked(
callingPackage)) {
return false;
@@ -1741,7 +1741,7 @@
continue;
}
- // Add providers only for the requested profile that are white-listed.
+ // Add providers only for the requested profile that are allowlisted.
final int providerProfileId = info.getProfile().getIdentifier();
if (providerProfileId == profileId
&& mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed(
@@ -3576,7 +3576,7 @@
@Override
public void onCrossProfileWidgetProvidersChanged(int userId, List<String> packages) {
final int parentId = mSecurityPolicy.getProfileParent(userId);
- // We care only if the white-listed package is in a profile of
+ // We care only if the allowlisted package is in a profile of
// the group parent as only the parent can add widgets from the
// profile and not the other way around.
if (parentId != userId) {
@@ -3600,7 +3600,7 @@
userId, null);
}
- // Remove widgets from hosts in parent user for packages not in the whitelist
+ // Remove widgets from hosts in parent user for packages not in the allowlist
final int removedCount = previousPackages.size();
for (int i = 0; i < removedCount; ++i) {
removeWidgetsForPackageLocked(previousPackages.valueAt(i),
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 57ffe04..d59c955 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -313,7 +313,7 @@
@NonNull ComponentName componentName, boolean compatMode,
boolean bindInstantServiceAllowed, int flags) {
// FLAG_AUGMENTED_AUTOFILL_REQUEST is set in the flags when standard autofill is disabled
- // but the package is whitelisted for augmented autofill
+ // but the package is allowlisted for augmented autofill
boolean forAugmentedAutofillOnly = (flags
& FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY) != 0;
if (!isEnabledLocked() && !forAugmentedAutofillOnly) {
@@ -322,7 +322,7 @@
if (!forAugmentedAutofillOnly && isAutofillDisabledLocked(componentName)) {
// Standard autofill is enabled, but service disabled autofill for this activity; that
- // means no session, unless the activity is whitelisted for augmented autofill
+ // means no session, unless the activity is allowlisted for augmented autofill
if (isWhitelistedForAugmentedAutofillLocked(componentName)) {
if (sDebug) {
Slog.d(TAG, "startSession(" + componentName + "): disabled by service but "
@@ -1379,7 +1379,7 @@
}
/**
- * Resets the augmented autofill whitelist.
+ * Resets the augmented autofill allowlist.
*/
@GuardedBy("mLock")
void resetAugmentedAutofillWhitelistLocked() {
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 533bbe6..92b8608 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -261,9 +261,13 @@
focusedValue != null && focusedValue.isText()
? focusedValue.getTextValue().toString() : null;
+ final InlineFillUi.InlineFillUiInfo inlineFillUiInfo =
+ new InlineFillUi.InlineFillUiInfo(request, focusedId, filterText,
+ remoteRenderService, userId, sessionId);
+
final InlineFillUi inlineFillUi =
InlineFillUi.forAugmentedAutofill(
- request, inlineSuggestionsData, focusedId, filterText,
+ inlineFillUiInfo, inlineSuggestionsData,
new InlineFillUi.InlineSuggestionUiCallback() {
@Override
public void autofill(Dataset dataset, int datasetIndex) {
@@ -305,15 +309,24 @@
}
@Override
- public void startIntentSender(IntentSender intentSender,
- Intent intent) {
+ public void authenticate(int requestId, int datasetIndex) {
+ Slog.e(TAG, "authenticate not implemented for augmented autofill");
+ }
+
+ @Override
+ public void startIntentSender(IntentSender intentSender) {
try {
- client.startIntentSender(intentSender, intent);
+ client.startIntentSender(intentSender, new Intent());
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException starting intent sender");
}
}
- }, onErrorCallback, remoteRenderService, userId, sessionId);
+
+ @Override
+ public void onError() {
+ onErrorCallback.run();
+ }
+ });
if (inlineSuggestionsCallback.apply(inlineFillUi)) {
mCallbacks.logAugmentedAutofillShown(sessionId, clientState);
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 1970b57..4d2e4f6 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -3008,14 +3008,36 @@
return false;
}
- InlineFillUi inlineFillUi = InlineFillUi.forAutofill(
- inlineSuggestionsRequest.get(), response, focusedId, filterText,
- /*uiCallback*/this, /*onErrorCallback*/ () -> {
- synchronized (mLock) {
- mInlineSessionController.setInlineFillUiLocked(
- InlineFillUi.emptyUi(focusedId));
+ final InlineFillUi.InlineFillUiInfo inlineFillUiInfo =
+ new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId,
+ filterText, remoteRenderService, userId, id);
+ InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response,
+ new InlineFillUi.InlineSuggestionUiCallback() {
+ @Override
+ public void autofill(@NonNull Dataset dataset, int datasetIndex) {
+ fill(response.getRequestId(), datasetIndex, dataset);
}
- }, remoteRenderService, userId, id);
+
+ @Override
+ public void authenticate(int requestId, int datasetIndex) {
+ Session.this.authenticate(response.getRequestId(), datasetIndex,
+ response.getAuthentication(), response.getClientState(),
+ /* authenticateInline= */ true);
+ }
+
+ @Override
+ public void startIntentSender(@NonNull IntentSender intentSender) {
+ Session.this.startIntentSender(intentSender, new Intent());
+ }
+
+ @Override
+ public void onError() {
+ synchronized (mLock) {
+ mInlineSessionController.setInlineFillUiLocked(
+ InlineFillUi.emptyUi(focusedId));
+ }
+ }
+ });
return mInlineSessionController.setInlineFillUiLocked(inlineFillUi);
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
index 25e9d5c..ff17590 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
@@ -20,7 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.Intent;
+import android.annotation.UserIdInt;
import android.content.IntentSender;
import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
@@ -32,6 +32,7 @@
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.inputmethod.InlineSuggestion;
+import android.view.inputmethod.InlineSuggestionInfo;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InlineSuggestionsResponse;
@@ -93,59 +94,72 @@
*/
@NonNull
public static InlineFillUi emptyUi(@NonNull AutofillId autofillId) {
- return new InlineFillUi(autofillId, new SparseArray<>(), null);
+ return new InlineFillUi(autofillId);
+ }
+
+ /**
+ * Encapsulates various arguments used by {@link #forAutofill} and {@link #forAugmentedAutofill}
+ */
+ public static class InlineFillUiInfo {
+
+ public int mUserId;
+ public int mSessionId;
+ public InlineSuggestionsRequest mInlineRequest;
+ public AutofillId mFocusedId;
+ public String mFilterText;
+ public RemoteInlineSuggestionRenderService mRemoteRenderService;
+
+ public InlineFillUiInfo(@NonNull InlineSuggestionsRequest inlineRequest,
+ @NonNull AutofillId focusedId, @NonNull String filterText,
+ @NonNull RemoteInlineSuggestionRenderService remoteRenderService,
+ @UserIdInt int userId, int sessionId) {
+ mUserId = userId;
+ mSessionId = sessionId;
+ mInlineRequest = inlineRequest;
+ mFocusedId = focusedId;
+ mFilterText = filterText;
+ mRemoteRenderService = remoteRenderService;
+ }
}
/**
* Returns an inline autofill UI for a field based on an Autofilll response.
*/
@NonNull
- public static InlineFillUi forAutofill(@NonNull InlineSuggestionsRequest request,
+ public static InlineFillUi forAutofill(@NonNull InlineFillUiInfo inlineFillUiInfo,
@NonNull FillResponse response,
- @NonNull AutofillId focusedViewId, @Nullable String filterText,
- @NonNull AutoFillUI.AutoFillUiCallback uiCallback,
- @NonNull Runnable onErrorCallback,
- @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
- int userId, int sessionId) {
-
- if (InlineSuggestionFactory.responseNeedAuthentication(response)) {
+ @NonNull InlineSuggestionUiCallback uiCallback) {
+ if (response.getAuthentication() != null && response.getInlinePresentation() != null) {
InlineSuggestion inlineAuthentication =
- InlineSuggestionFactory.createInlineAuthentication(request, response,
- uiCallback, onErrorCallback, remoteRenderService, userId, sessionId);
- return new InlineFillUi(focusedViewId, inlineAuthentication, filterText);
+ InlineSuggestionFactory.createInlineAuthentication(inlineFillUiInfo, response,
+ uiCallback);
+ return new InlineFillUi(inlineFillUiInfo, inlineAuthentication);
} else if (response.getDatasets() != null) {
SparseArray<Pair<Dataset, InlineSuggestion>> inlineSuggestions =
- InlineSuggestionFactory.createAutofillInlineSuggestions(request,
- response.getRequestId(),
- response.getDatasets(), focusedViewId, uiCallback, onErrorCallback,
- remoteRenderService, userId, sessionId);
- return new InlineFillUi(focusedViewId, inlineSuggestions, filterText);
+ InlineSuggestionFactory.createInlineSuggestions(inlineFillUiInfo,
+ InlineSuggestionInfo.SOURCE_AUTOFILL, response.getDatasets(),
+ uiCallback);
+ return new InlineFillUi(inlineFillUiInfo, inlineSuggestions);
}
- return new InlineFillUi(focusedViewId, new SparseArray<>(), filterText);
+ return new InlineFillUi(inlineFillUiInfo, new SparseArray<>());
}
/**
* Returns an inline autofill UI for a field based on an Autofilll response.
*/
@NonNull
- public static InlineFillUi forAugmentedAutofill(@NonNull InlineSuggestionsRequest request,
+ public static InlineFillUi forAugmentedAutofill(@NonNull InlineFillUiInfo inlineFillUiInfo,
@NonNull List<Dataset> datasets,
- @NonNull AutofillId focusedViewId, @Nullable String filterText,
- @NonNull InlineSuggestionUiCallback uiCallback,
- @NonNull Runnable onErrorCallback,
- @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
- int userId, int sessionId) {
+ @NonNull InlineSuggestionUiCallback uiCallback) {
SparseArray<Pair<Dataset, InlineSuggestion>> inlineSuggestions =
- InlineSuggestionFactory.createAugmentedAutofillInlineSuggestions(request, datasets,
- focusedViewId,
- uiCallback, onErrorCallback, remoteRenderService, userId, sessionId);
- return new InlineFillUi(focusedViewId, inlineSuggestions, filterText);
+ InlineSuggestionFactory.createInlineSuggestions(inlineFillUiInfo,
+ InlineSuggestionInfo.SOURCE_PLATFORM, datasets, uiCallback);
+ return new InlineFillUi(inlineFillUiInfo, inlineSuggestions);
}
- InlineFillUi(@NonNull AutofillId autofillId,
- @NonNull SparseArray<Pair<Dataset, InlineSuggestion>> inlineSuggestions,
- @Nullable String filterText) {
- mAutofillId = autofillId;
+ private InlineFillUi(@Nullable InlineFillUiInfo inlineFillUiInfo,
+ @NonNull SparseArray<Pair<Dataset, InlineSuggestion>> inlineSuggestions) {
+ mAutofillId = inlineFillUiInfo.mFocusedId;
int size = inlineSuggestions.size();
mDatasets = new ArrayList<>(size);
mInlineSuggestions = new ArrayList<>(size);
@@ -154,16 +168,26 @@
mDatasets.add(value.first);
mInlineSuggestions.add(value.second);
}
- mFilterText = filterText;
+ mFilterText = inlineFillUiInfo.mFilterText;
}
- InlineFillUi(@NonNull AutofillId autofillId, InlineSuggestion inlineSuggestion,
- @Nullable String filterText) {
- mAutofillId = autofillId;
+ private InlineFillUi(@NonNull InlineFillUiInfo inlineFillUiInfo,
+ @NonNull InlineSuggestion inlineSuggestion) {
+ mAutofillId = inlineFillUiInfo.mFocusedId;
mDatasets = null;
mInlineSuggestions = new ArrayList<>();
mInlineSuggestions.add(inlineSuggestion);
- mFilterText = filterText;
+ mFilterText = inlineFillUiInfo.mFilterText;
+ }
+
+ /**
+ * Only used for constructing an empty InlineFillUi with {@link #emptyUi}
+ */
+ private InlineFillUi(@NonNull AutofillId focusedId) {
+ mAutofillId = focusedId;
+ mDatasets = new ArrayList<>(0);
+ mInlineSuggestions = new ArrayList<>(0);
+ mFilterText = null;
}
@NonNull
@@ -295,9 +319,21 @@
void autofill(@NonNull Dataset dataset, int datasetIndex);
/**
+ * Callback to authenticate a dataset.
+ *
+ * <p>Only implemented by regular autofill for now.</p>
+ */
+ void authenticate(int requestId, int datasetIndex);
+
+ /**
* Callback to start Intent in client app.
*/
- void startIntentSender(@NonNull IntentSender intentSender, @NonNull Intent intent);
+ void startIntentSender(@NonNull IntentSender intentSender);
+
+ /**
+ * Callback on errors.
+ */
+ void onError();
}
/**
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index 8fcb8aa..b15d07b 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -20,184 +20,98 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.os.IBinder;
import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
import android.service.autofill.InlinePresentation;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
-import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.inputmethod.InlineSuggestion;
import android.view.inputmethod.InlineSuggestionInfo;
import android.view.inputmethod.InlineSuggestionsRequest;
-import android.view.inputmethod.InlineSuggestionsResponse;
import android.widget.inline.InlinePresentationSpec;
import com.android.internal.view.inline.IInlineContentProvider;
-import com.android.server.autofill.RemoteInlineSuggestionRenderService;
import java.util.List;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
final class InlineSuggestionFactory {
private static final String TAG = "InlineSuggestionFactory";
- public static boolean responseNeedAuthentication(@NonNull FillResponse response) {
- return response.getAuthentication() != null && response.getInlinePresentation() != null;
- }
-
public static InlineSuggestion createInlineAuthentication(
- @NonNull InlineSuggestionsRequest request, @NonNull FillResponse response,
- @NonNull AutoFillUI.AutoFillUiCallback client, @NonNull Runnable onErrorCallback,
- @Nullable RemoteInlineSuggestionRenderService remoteRenderService, int userId,
- int sessionId) {
- final BiConsumer<Dataset, Integer> onClickFactory = (dataset, datasetIndex) -> {
- client.authenticate(response.getRequestId(),
- datasetIndex, response.getAuthentication(), response.getClientState(),
- /* authenticateInline= */ true);
- };
- final Consumer<IntentSender> intentSenderConsumer = (intentSender) ->
- client.startIntentSender(intentSender, new Intent());
+ @NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo, @NonNull FillResponse response,
+ @NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) {
InlinePresentation inlineAuthentication = response.getInlinePresentation();
- return createInlineAuthSuggestion(
- mergedInlinePresentation(request, 0, inlineAuthentication),
- remoteRenderService, userId, sessionId,
- onClickFactory, onErrorCallback, intentSenderConsumer,
- request.getHostInputToken(), request.getHostDisplayId());
+ final int requestId = response.getRequestId();
+
+ return createInlineSuggestion(inlineFillUiInfo, InlineSuggestionInfo.SOURCE_AUTOFILL,
+ InlineSuggestionInfo.TYPE_ACTION, () -> uiCallback.authenticate(requestId,
+ AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED),
+ mergedInlinePresentation(inlineFillUiInfo.mInlineRequest, 0, inlineAuthentication),
+ uiCallback);
}
/**
- * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by the
- * autofill service, potentially filtering the datasets.
+ * Creates an array of {@link InlineSuggestion}s with the {@code datasets} provided by either
+ * regular/augmented autofill services.
*/
@Nullable
- public static SparseArray<Pair<Dataset, InlineSuggestion>> createAutofillInlineSuggestions(
- @NonNull InlineSuggestionsRequest request, int requestId,
+ public static SparseArray<Pair<Dataset, InlineSuggestion>> createInlineSuggestions(
+ @NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo,
+ @NonNull @InlineSuggestionInfo.Source String suggestionSource,
@NonNull List<Dataset> datasets,
- @NonNull AutofillId autofillId,
- @NonNull AutoFillUI.AutoFillUiCallback client, @NonNull Runnable onErrorCallback,
- @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
- int userId, int sessionId) {
- if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");
- final Consumer<IntentSender> intentSenderConsumer = (intentSender) ->
- client.startIntentSender(intentSender, new Intent());
- final BiConsumer<Dataset, Integer> onClickFactory = (dataset, datasetIndex) -> {
- client.fill(requestId, datasetIndex, dataset);
- };
+ @NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) {
+ if (sDebug) Slog.d(TAG, "createInlineSuggestions(source=" + suggestionSource + ") called");
- return createInlineSuggestionsInternal(/* isAugmented= */ false, request,
- datasets, autofillId,
- onErrorCallback, onClickFactory, intentSenderConsumer, remoteRenderService, userId,
- sessionId);
- }
-
- /**
- * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by augmented
- * autofill service.
- */
- @Nullable
- public static SparseArray<Pair<Dataset, InlineSuggestion>>
- createAugmentedAutofillInlineSuggestions(
- @NonNull InlineSuggestionsRequest request, @NonNull List<Dataset> datasets,
- @NonNull AutofillId autofillId,
- @NonNull InlineFillUi.InlineSuggestionUiCallback inlineSuggestionUiCallback,
- @NonNull Runnable onErrorCallback,
- @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
- int userId, int sessionId) {
- if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
- return createInlineSuggestionsInternal(/* isAugmented= */ true, request,
- datasets, autofillId, onErrorCallback,
- (dataset, datasetIndex) ->
- inlineSuggestionUiCallback.autofill(dataset, datasetIndex),
- (intentSender) ->
- inlineSuggestionUiCallback.startIntentSender(intentSender, new Intent()),
- remoteRenderService, userId, sessionId);
- }
-
- @Nullable
- private static SparseArray<Pair<Dataset, InlineSuggestion>> createInlineSuggestionsInternal(
- boolean isAugmented, @NonNull InlineSuggestionsRequest request,
- @NonNull List<Dataset> datasets, @NonNull AutofillId autofillId,
- @NonNull Runnable onErrorCallback, @NonNull BiConsumer<Dataset, Integer> onClickFactory,
- @NonNull Consumer<IntentSender> intentSenderConsumer,
- @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
- int userId, int sessionId) {
+ final InlineSuggestionsRequest request = inlineFillUiInfo.mInlineRequest;
SparseArray<Pair<Dataset, InlineSuggestion>> response = new SparseArray<>(datasets.size());
for (int datasetIndex = 0; datasetIndex < datasets.size(); datasetIndex++) {
final Dataset dataset = datasets.get(datasetIndex);
- final int fieldIndex = dataset.getFieldIds().indexOf(autofillId);
+ final int fieldIndex = dataset.getFieldIds().indexOf(inlineFillUiInfo.mFocusedId);
if (fieldIndex < 0) {
- Slog.w(TAG, "AutofillId=" + autofillId + " not found in dataset");
+ Slog.w(TAG, "AutofillId=" + inlineFillUiInfo.mFocusedId + " not found in dataset");
continue;
}
- final InlinePresentation inlinePresentation = dataset.getFieldInlinePresentation(
- fieldIndex);
+
+ final InlinePresentation inlinePresentation =
+ dataset.getFieldInlinePresentation(fieldIndex);
if (inlinePresentation == null) {
Slog.w(TAG, "InlinePresentation not found in dataset");
continue;
}
- InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset,
- datasetIndex,
+
+ final String suggestionType =
+ dataset.getAuthentication() == null ? InlineSuggestionInfo.TYPE_SUGGESTION
+ : InlineSuggestionInfo.TYPE_ACTION;
+ final int index = datasetIndex;
+
+ InlineSuggestion inlineSuggestion = createInlineSuggestion(
+ inlineFillUiInfo, suggestionSource, suggestionType,
+ () -> uiCallback.autofill(dataset, index),
mergedInlinePresentation(request, datasetIndex, inlinePresentation),
- onClickFactory, remoteRenderService, userId, sessionId,
- onErrorCallback, intentSenderConsumer,
- request.getHostInputToken(), request.getHostDisplayId());
+ uiCallback);
response.append(datasetIndex, Pair.create(dataset, inlineSuggestion));
}
+
return response;
}
- private static InlineSuggestion createInlineSuggestion(boolean isAugmented,
- @NonNull Dataset dataset, int datasetIndex,
+ private static InlineSuggestion createInlineSuggestion(
+ @NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo,
+ @NonNull @InlineSuggestionInfo.Source String suggestionSource,
+ @NonNull @InlineSuggestionInfo.Type String suggestionType,
+ @NonNull Runnable onClickAction,
@NonNull InlinePresentation inlinePresentation,
- @NonNull BiConsumer<Dataset, Integer> onClickFactory,
- @NonNull RemoteInlineSuggestionRenderService remoteRenderService,
- int userId, int sessionId,
- @NonNull Runnable onErrorCallback, @NonNull Consumer<IntentSender> intentSenderConsumer,
- @Nullable IBinder hostInputToken,
- int displayId) {
- final String suggestionSource = isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
- : InlineSuggestionInfo.SOURCE_AUTOFILL;
- final String suggestionType =
- dataset.getAuthentication() == null ? InlineSuggestionInfo.TYPE_SUGGESTION
- : InlineSuggestionInfo.TYPE_ACTION;
+ @NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) {
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(), suggestionSource,
inlinePresentation.getAutofillHints(), suggestionType,
inlinePresentation.isPinned());
- final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
- createInlineContentProvider(inlinePresentation,
- () -> onClickFactory.accept(dataset, datasetIndex), onErrorCallback,
- intentSenderConsumer, remoteRenderService, userId, sessionId,
- hostInputToken, displayId));
-
- return inlineSuggestion;
- }
-
- private static InlineSuggestion createInlineAuthSuggestion(
- @NonNull InlinePresentation inlinePresentation,
- @NonNull RemoteInlineSuggestionRenderService remoteRenderService,
- int userId, int sessionId,
- @NonNull BiConsumer<Dataset, Integer> onClickFactory, @NonNull Runnable onErrorCallback,
- @NonNull Consumer<IntentSender> intentSenderConsumer,
- @Nullable IBinder hostInputToken, int displayId) {
- final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
- inlinePresentation.getInlinePresentationSpec(),
- InlineSuggestionInfo.SOURCE_AUTOFILL, inlinePresentation.getAutofillHints(),
- InlineSuggestionInfo.TYPE_ACTION, inlinePresentation.isPinned());
-
return new InlineSuggestion(inlineSuggestionInfo,
- createInlineContentProvider(inlinePresentation,
- () -> onClickFactory.accept(null,
- AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED),
- onErrorCallback, intentSenderConsumer, remoteRenderService, userId,
- sessionId, hostInputToken, displayId));
+ createInlineContentProvider(inlineFillUiInfo, inlinePresentation,
+ onClickAction, uiCallback));
}
/**
@@ -216,25 +130,20 @@
inlinePresentation.getInlinePresentationSpec().getMinSize(),
inlinePresentation.getInlinePresentationSpec().getMaxSize()).setStyle(
specFromHost.getStyle()).build();
+
return new InlinePresentation(inlinePresentation.getSlice(), mergedInlinePresentation,
inlinePresentation.isPinned());
}
private static IInlineContentProvider createInlineContentProvider(
+ @NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo,
@NonNull InlinePresentation inlinePresentation, @Nullable Runnable onClickAction,
- @NonNull Runnable onErrorCallback,
- @NonNull Consumer<IntentSender> intentSenderConsumer,
- @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
- int userId, int sessionId,
- @Nullable IBinder hostInputToken,
- int displayId) {
- RemoteInlineSuggestionViewConnector
- remoteInlineSuggestionViewConnector = new RemoteInlineSuggestionViewConnector(
- remoteRenderService, userId, sessionId, inlinePresentation, hostInputToken,
- displayId, onClickAction, onErrorCallback, intentSenderConsumer);
- InlineContentProviderImpl inlineContentProvider = new InlineContentProviderImpl(
- remoteInlineSuggestionViewConnector, null);
- return inlineContentProvider;
+ @NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) {
+ RemoteInlineSuggestionViewConnector remoteInlineSuggestionViewConnector =
+ new RemoteInlineSuggestionViewConnector(inlineFillUiInfo, inlinePresentation,
+ onClickAction, uiCallback);
+
+ return new InlineContentProviderImpl(remoteInlineSuggestionViewConnector, null);
}
private InlineSuggestionFactory() {
diff --git a/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java b/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java
index 7257255..46d435d 100644
--- a/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java
+++ b/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java
@@ -57,24 +57,20 @@
private final Consumer<IntentSender> mStartIntentSenderFromClientApp;
RemoteInlineSuggestionViewConnector(
- @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
- int userId, int sessionId,
+ @NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo,
@NonNull InlinePresentation inlinePresentation,
- @Nullable IBinder hostInputToken,
- int displayId,
@NonNull Runnable onAutofillCallback,
- @NonNull Runnable onErrorCallback,
- @NonNull Consumer<IntentSender> startIntentSenderFromClientApp) {
- mRemoteRenderService = remoteRenderService;
+ @NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) {
+ mRemoteRenderService = inlineFillUiInfo.mRemoteRenderService;
mInlinePresentation = inlinePresentation;
- mHostInputToken = hostInputToken;
- mDisplayId = displayId;
- mUserId = userId;
- mSessionId = sessionId;
+ mHostInputToken = inlineFillUiInfo.mInlineRequest.getHostInputToken();
+ mDisplayId = inlineFillUiInfo.mInlineRequest.getHostDisplayId();
+ mUserId = inlineFillUiInfo.mUserId;
+ mSessionId = inlineFillUiInfo.mSessionId;
mOnAutofillCallback = onAutofillCallback;
- mOnErrorCallback = onErrorCallback;
- mStartIntentSenderFromClientApp = startIntentSenderFromClientApp;
+ mOnErrorCallback = uiCallback::onError;
+ mStartIntentSenderFromClientApp = uiCallback::startIntentSender;
}
/**
diff --git a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
index b1c356a..fd573d5 100644
--- a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
@@ -174,7 +174,7 @@
}
}
- /** Returns a set with the whitelisted transports. */
+ /** Returns a set with the allowlisted transports. */
Set<ComponentName> getTransportWhitelist() {
return mTransportWhitelist;
}
@@ -591,7 +591,7 @@
}
}
- /** Transport has to be whitelisted and privileged. */
+ /** Transport has to be allowlisted and privileged. */
private boolean isTransportTrusted(ComponentName transport) {
if (!mTransportWhitelist.contains(transport)) {
Slog.w(
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java
index ca89f7f..0eb3ea3 100644
--- a/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java
@@ -664,7 +664,7 @@
return;
}
// TODO (b/147705255): Remove when binder calls to IBackupTransport are not blocking
- // In short-term, blocking calls are OK as the transports come from the whitelist at
+ // In short-term, blocking calls are OK as the transports come from the allowlist at
// {@link SystemConfig#getBackupTransportWhitelist()}
Binder.allowBlocking(binder);
transportClient.onServiceConnected(binder);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 7b9728cb..6ca99d1 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -17,7 +17,8 @@
package com.android.server.companion;
-import static com.android.internal.util.CollectionUtils.size;
+import static com.android.internal.util.CollectionUtils.emptyIfNull;
+import static com.android.internal.util.CollectionUtils.forEach;
import static com.android.internal.util.FunctionalUtils.uncheckExceptions;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
@@ -72,6 +73,7 @@
import android.util.Slog;
import android.util.Xml;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
import com.android.internal.content.PackageMonitor;
import com.android.internal.infra.AndroidFuture;
@@ -81,11 +83,11 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DumpUtils;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
import com.android.server.wm.ActivityTaskManagerInternal;
import org.xmlpull.v1.XmlPullParser;
@@ -96,8 +98,10 @@
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -143,6 +147,9 @@
private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private @Nullable Set<Association> mCachedAssociations = null;
+
public CompanionDeviceManagerService(Context context) {
super(context);
mImpl = new CompanionDeviceManagerImpl();
@@ -170,14 +177,14 @@
public void onPackageRemoved(String packageName, int uid) {
updateAssociations(
as -> CollectionUtils.filter(as,
- a -> !Objects.equals(a.companionAppPackage, packageName)),
+ a -> !Objects.equals(a.getPackageName(), packageName)),
getChangingUserId());
}
@Override
public void onPackageModified(String packageName) {
int userId = getChangingUserId();
- if (!ArrayUtils.isEmpty(readAllAssociations(userId, packageName))) {
+ if (!ArrayUtils.isEmpty(getAllAssociations(userId, packageName))) {
updateSpecialAccessPermissionForAssociatedPackage(packageName, userId);
}
}
@@ -193,13 +200,13 @@
@Override
public void onUserUnlocking(@NonNull TargetUser user) {
int userHandle = user.getUserIdentifier();
- Set<Association> associations = readAllAssociations(userHandle);
+ Set<Association> associations = getAllAssociations(userHandle);
if (associations == null || associations.isEmpty()) {
return;
}
Set<String> companionAppPackages = new HashSet<>();
for (Association association : associations) {
- companionAppPackages.add(association.companionAppPackage);
+ companionAppPackages.add(association.getPackageName());
}
ActivityTaskManagerInternal atmInternal = LocalServices.getService(
ActivityTaskManagerInternal.class);
@@ -223,16 +230,16 @@
}
try {
- Set<Association> associations = readAllAssociations(userId);
+ Set<Association> associations = getAllAssociations(userId);
if (associations == null) {
continue;
}
for (Association a : associations) {
try {
- int uid = pm.getPackageUidAsUser(a.companionAppPackage, userId);
- exemptFromAutoRevoke(a.companionAppPackage, uid);
+ int uid = pm.getPackageUidAsUser(a.getPackageName(), userId);
+ exemptFromAutoRevoke(a.getPackageName(), uid);
} catch (PackageManager.NameNotFoundException e) {
- Log.w(LOG_TAG, "Unknown companion package: " + a.companionAppPackage, e);
+ Log.w(LOG_TAG, "Unknown companion package: " + a.getPackageName(), e);
}
}
} finally {
@@ -343,8 +350,18 @@
checkUsesFeature(callingPackage, getCallingUserId());
}
return new ArrayList<>(CollectionUtils.map(
- readAllAssociations(userId, callingPackage),
- a -> a.deviceAddress));
+ getAllAssociations(userId, callingPackage),
+ a -> a.getDeviceMacAddress()));
+ }
+
+ @Override
+ public List<Association> getAssociationsForUser(int userId) {
+ if (!callerCanManageCompanionDevices()) {
+ throw new SecurityException("Caller must hold "
+ + android.Manifest.permission.MANAGE_COMPANION_DEVICES);
+ }
+
+ return new ArrayList<>(getAllAssociations(userId, null /* packageFilter */));
}
//TODO also revoke notification access
@@ -428,14 +445,14 @@
}
return CollectionUtils.any(
- readAllAssociations(userId, packageName),
- a -> Objects.equals(a.deviceAddress, macAddress));
+ getAllAssociations(userId, packageName),
+ a -> Objects.equals(a.getDeviceMacAddress(), macAddress));
}
private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
checkCallerIsSystemOr(callingPackage);
int userId = getCallingUserId();
- checkState(!ArrayUtils.isEmpty(readAllAssociations(userId, callingPackage)),
+ checkState(!ArrayUtils.isEmpty(getAllAssociations(userId, callingPackage)),
"App must have an association before calling this API");
checkUsesFeature(callingPackage, userId);
}
@@ -463,6 +480,25 @@
throws RemoteException {
new ShellCmd().exec(this, in, out, err, args, callback, resultReceiver);
}
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd,
+ @NonNull PrintWriter fout,
+ @Nullable String[] args) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), LOG_TAG, fout)) {
+ return;
+ }
+
+ fout.append("Companion Device Associations:").append('\n');
+ synchronized (mLock) {
+ forEach(mCachedAssociations, a -> {
+ fout.append(" ")
+ .append("u").append("" + a.getUserId()).append(": ")
+ .append(a.getPackageName()).append(" - ")
+ .append(a.getDeviceMacAddress()).append('\n');
+ });
+ }
+ }
}
private static int getCallingUserId() {
@@ -479,7 +515,7 @@
void addAssociation(Association association) {
updateSpecialAccessPermissionForAssociatedPackage(
- association.companionAppPackage, association.userId);
+ association.getPackageName(), association.getUserId());
recordAssociation(association);
}
@@ -572,20 +608,37 @@
private void updateAssociations(Function<Set<Association>, Set<Association>> update,
int userId) {
- final AtomicFile file = getStorageFileForUser(userId);
- synchronized (file) {
- Set<Association> associations = readAllAssociations(userId);
- final Set<Association> old = CollectionUtils.copyOf(associations);
+ synchronized (mLock) {
+ final Set<Association> old = getAllAssociations(userId);
+ Set<Association> associations = new ArraySet<>(old);
associations = update.apply(associations);
- if (size(old) == size(associations)) return;
- Set<Association> finalAssociations = associations;
Set<String> companionAppPackages = new HashSet<>();
- for (Association association : finalAssociations) {
- companionAppPackages.add(association.companionAppPackage);
+ for (Association association : associations) {
+ companionAppPackages.add(association.getPackageName());
}
- file.write((out) -> {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Updating associations: " + old + " --> " + associations);
+ }
+ mCachedAssociations = Collections.unmodifiableSet(associations);
+ BackgroundThread.getHandler().sendMessage(PooledLambda.obtainMessage(
+ CompanionDeviceManagerService::persistAssociations,
+ this, associations, userId));
+
+ ActivityTaskManagerInternal atmInternal = LocalServices.getService(
+ ActivityTaskManagerInternal.class);
+ atmInternal.setCompanionAppPackages(userId, companionAppPackages);
+ }
+ }
+
+ private void persistAssociations(Set<Association> associations, int userId) {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Writing associations to disk: " + associations);
+ }
+ final AtomicFile file = getStorageFileForUser(userId);
+ synchronized (file) {
+ file.write(out -> {
XmlSerializer xml = Xml.newSerializer();
try {
xml.setOutput(out, StandardCharsets.UTF_8.name());
@@ -593,10 +646,10 @@
xml.startDocument(null, true);
xml.startTag(null, XML_TAG_ASSOCIATIONS);
- CollectionUtils.forEach(finalAssociations, association -> {
+ forEach(associations, association -> {
xml.startTag(null, XML_TAG_ASSOCIATION)
- .attribute(null, XML_ATTR_PACKAGE, association.companionAppPackage)
- .attribute(null, XML_ATTR_DEVICE, association.deviceAddress)
+ .attribute(null, XML_ATTR_PACKAGE, association.getPackageName())
+ .attribute(null, XML_ATTR_DEVICE, association.getDeviceMacAddress())
.endTag(null, XML_TAG_ASSOCIATION);
});
@@ -606,11 +659,7 @@
Slog.e(LOG_TAG, "Error while writing associations file", e);
throw ExceptionUtils.propagate(e);
}
-
});
- ActivityTaskManagerInternal atmInternal = LocalServices.getService(
- ActivityTaskManagerInternal.class);
- atmInternal.setCompanionAppPackages(userId, companionAppPackages);
}
}
@@ -623,12 +672,27 @@
}
@Nullable
- private Set<Association> readAllAssociations(int userId) {
- return readAllAssociations(userId, null);
+ private Set<Association> getAllAssociations(int userId) {
+ synchronized (mLock) {
+ if (mCachedAssociations == null) {
+ mCachedAssociations = Collections.unmodifiableSet(
+ emptyIfNull(readAllAssociations(userId)));
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Read associations from disk: " + mCachedAssociations);
+ }
+ }
+ return mCachedAssociations;
+ }
}
@Nullable
- private Set<Association> readAllAssociations(int userId, @Nullable String packageFilter) {
+ private Set<Association> getAllAssociations(int userId, @Nullable String packageFilter) {
+ return CollectionUtils.filter(
+ getAllAssociations(userId),
+ a -> Objects.equals(packageFilter, a.getPackageName()));
+ }
+
+ private Set<Association> readAllAssociations(int userId) {
final AtomicFile file = getStorageFileForUser(userId);
if (!file.getBaseFile().exists()) return null;
@@ -647,7 +711,6 @@
final String deviceAddress = parser.getAttributeValue(null, XML_ATTR_DEVICE);
if (appPackage == null || deviceAddress == null) continue;
- if (packageFilter != null && !packageFilter.equals(appPackage)) continue;
result = ArrayUtils.add(result,
new Association(userId, deviceAddress, appPackage));
@@ -675,10 +738,10 @@
public int onCommand(String cmd) {
switch (cmd) {
case "list": {
- CollectionUtils.forEach(
- readAllAssociations(getNextArgInt()),
+ forEach(
+ getAllAssociations(getNextArgInt()),
a -> getOutPrintWriter()
- .println(a.companionAppPackage + " " + a.deviceAddress));
+ .println(a.getPackageName() + " " + a.getDeviceMacAddress()));
} break;
case "associate": {
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index e742015..0be485b 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -885,11 +885,11 @@
synchronized (mGlobalWhitelistStateLock) {
packageWhitelisted = isWhitelisted(userId, packageName);
if (!packageWhitelisted) {
- // Full package is not whitelisted: check individual components first
+ // Full package is not allowlisted: check individual components first
whitelistedComponents = getWhitelistedComponents(userId, packageName);
if (whitelistedComponents == null
&& packageName.equals(mServicePackages.get(userId))) {
- // No components whitelisted either, but let it go because it's the
+ // No components allowlisted either, but let it go because it's the
// service's own package
if (verbose) Slog.v(TAG, "getOptionsForPackage() lite for " + packageName);
return new ContentCaptureOptions(mDevCfgLoggingLevel);
@@ -897,7 +897,7 @@
}
} // synchronized
- // Restrict what temporary services can whitelist
+ // Restrict what temporary services can allowlist
if (Build.IS_USER && mServiceNameResolver.isTemporary(userId)) {
if (!packageName.equals(mServicePackages.get(userId))) {
Slog.w(TAG, "Ignoring package " + packageName + " while using temporary "
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 9486b0d..ea68e19 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -93,7 +93,7 @@
* Reference to the remote service.
*
* <p>It's set in the constructor, but it's also updated when the service's updated in the
- * master's cache (for example, because a temporary service was set).
+ * main service's cache (for example, because a temporary service was set).
*/
@GuardedBy("mLock")
@Nullable
@@ -198,7 +198,7 @@
void onConnected() {
synchronized (mLock) {
if (mZombie) {
- // Sanity check - shouldn't happen
+ // Validity check - shouldn't happen
if (mRemoteService == null) {
Slog.w(TAG, "Cannot ressurect sessions because remote service is null");
return;
@@ -571,7 +571,7 @@
}
/**
- * Resets the content capture whitelist.
+ * Resets the content capture allowlist.
*/
@GuardedBy("mLock")
private void resetContentCaptureWhitelistLocked() {
@@ -598,7 +598,7 @@
mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities);
writeSetWhitelistEvent(getServiceComponentName(), packages, activities);
- // Must disable session that are not the whitelist anymore...
+ // Must disable session that are not the allowlist anymore...
final int numSessions = mSessions.size();
if (numSessions <= 0) return;
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index cf9324c..0c56d46 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -32,11 +32,16 @@
import android.content.pm.PackageManager.ResolveInfoFlags;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.IBinder;
+import android.os.Looper;
import android.os.PersistableBundle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.pm.PackageList;
import com.android.server.pm.PackageSetting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -46,6 +51,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.Collection;
import java.util.List;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -986,4 +992,72 @@
* Returns {@code true} if the package is suspending any packages for the user.
*/
public abstract boolean isSuspendingAnyPackages(String suspendingPackage, int userId);
+
+ /**
+ * Register to listen for loading progress of an installed package.
+ * @param packageName The name of the installed package
+ * @param callback To loading reporting progress
+ * @param userId The user under which to check.
+ * @return Whether the registration was successful. It can fail if the package has not been
+ * installed yet.
+ */
+ public abstract boolean registerInstalledLoadingProgressCallback(@NonNull String packageName,
+ @NonNull InstalledLoadingProgressCallback callback, int userId);
+
+ /**
+ * Unregister to stop listening to loading progress of an installed package
+ * @param packageName The name of the installed package
+ * @param callback To unregister
+ * @return True if the callback is removed from registered callback list. False is the callback
+ * does not exist on the registered callback list, which can happen if the callback has
+ * already been unregistered.
+ */
+ public abstract boolean unregisterInstalledLoadingProgressCallback(@NonNull String packageName,
+ @NonNull InstalledLoadingProgressCallback callback);
+
+ /**
+ * Callback to listen for loading progress of a package installed on Incremental File System.
+ */
+ public abstract static class InstalledLoadingProgressCallback {
+ final LoadingProgressCallbackBinder mBinder = new LoadingProgressCallbackBinder();
+ final Executor mExecutor;
+ /**
+ * Default constructor that should always be called on subclass instantiation
+ * @param handler To dispatch callback events through. If null, the main thread
+ * handler will be used.
+ */
+ public InstalledLoadingProgressCallback(@Nullable Handler handler) {
+ if (handler == null) {
+ handler = new Handler(Looper.getMainLooper());
+ }
+ mExecutor = new HandlerExecutor(handler);
+ }
+
+ /**
+ * Binder used by Package Manager Service to register as a callback
+ * @return the binder object of IPackageLoadingProgressCallback
+ */
+ public final @NonNull IBinder getBinder() {
+ return mBinder;
+ }
+
+ /**
+ * Report loading progress of an installed package.
+ *
+ * @param progress Loading progress between [0, 1] for the registered package.
+ */
+ public abstract void onLoadingProgressChanged(float progress);
+
+ private class LoadingProgressCallbackBinder extends
+ android.content.pm.IPackageLoadingProgressCallback.Stub {
+ @Override
+ public void onPackageLoadingProgressChanged(float progress) {
+ mExecutor.execute(PooledLambda.obtainRunnable(
+ InstalledLoadingProgressCallback::onLoadingProgressChanged,
+ InstalledLoadingProgressCallback.this,
+ progress).recycleOnUse());
+ }
+ }
+ }
+
}
diff --git a/services/core/java/android/os/UserManagerInternal.java b/services/core/java/android/os/UserManagerInternal.java
index 61e8128..76b5ba5 100644
--- a/services/core/java/android/os/UserManagerInternal.java
+++ b/services/core/java/android/os/UserManagerInternal.java
@@ -227,6 +227,14 @@
public abstract @NonNull List<UserInfo> getUsers(boolean excludeDying);
/**
+ * Internal implementation of getUsers does not check permissions.
+ * This improves performance for calls from inside system server which already have permissions
+ * checked.
+ */
+ public abstract @NonNull List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying,
+ boolean excludePreCreated);
+
+ /**
* Checks if the {@code callingUserId} and {@code targetUserId} are same or in same group
* and that the {@code callingUserId} is not a profile and {@code targetUserId} is enabled.
*
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 0d79240..4e405cc 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -418,7 +418,8 @@
if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) {
String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME);
if (DBG) {
- Slog.d(TAG, "Bluetooth Adapter name changed to " + newName);
+ Slog.d(TAG, "Bluetooth Adapter name changed to " + newName + " by "
+ + mContext.getPackageName());
}
if (newName != null) {
storeNameAndAddress(newName, null);
diff --git a/services/core/java/com/android/server/CertBlacklister.java b/services/core/java/com/android/server/CertBlacklister.java
index 8b167d7..c16378b 100644
--- a/services/core/java/com/android/server/CertBlacklister.java
+++ b/services/core/java/com/android/server/CertBlacklister.java
@@ -125,14 +125,14 @@
}
private void registerObservers(ContentResolver cr) {
- // set up the public key blacklist observer
+ // set up the public key denylist observer
cr.registerContentObserver(
Settings.Secure.getUriFor(PUBKEY_BLACKLIST_KEY),
true,
buildPubkeyObserver(cr)
);
- // set up the serial number blacklist observer
+ // set up the serial number denylist observer
cr.registerContentObserver(
Settings.Secure.getUriFor(SERIAL_BLACKLIST_KEY),
true,
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index bc79a6a..8a1baf2 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -5875,10 +5875,6 @@
return nai == getDefaultNetwork();
}
- private boolean isDefaultRequest(NetworkRequestInfo nri) {
- return nri.request.requestId == mDefaultRequest.requestId;
- }
-
// TODO : remove this method. It's a stopgap measure to help sheperding a number of dependent
// changes that would conflict throughout the automerger graph. Having this method temporarily
// helps with the process of going through with all these dependent changes across the entire
@@ -6240,7 +6236,7 @@
final int vpnAppUid = nai.networkCapabilities.getOwnerUid();
// TODO: this create a window of opportunity for apps to receive traffic between the time
// when the old rules are removed and the time when new rules are added. To fix this,
- // make eBPF support two whitelisted interfaces so here new rules can be added before the
+ // make eBPF support two allowlisted interfaces so here new rules can be added before the
// old rules are being removed.
if (wasFiltering) {
mPermissionMonitor.onVpnUidRangesRemoved(oldIface, ranges, vpnAppUid);
diff --git a/services/core/java/com/android/server/EntropyMixer.java b/services/core/java/com/android/server/EntropyMixer.java
index 5e6e9d34..c56cef2 100644
--- a/services/core/java/com/android/server/EntropyMixer.java
+++ b/services/core/java/com/android/server/EntropyMixer.java
@@ -52,9 +52,9 @@
* entropy estimate is not increased. This is to avoid having to trust/verify
* the quality and authenticity of the "randomness" of the HW RNG.
*
- * <p>This class was modeled after the script in
- * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html">man
- * 4 random</a>.
+ * <p>This class was modeled after the script in the
+ * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html">
+ * random(4) manual page</a>.
*/
public class EntropyMixer extends Binder {
private static final String TAG = "EntropyMixer";
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index ee794ba..ea14fad 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -373,7 +373,7 @@
*/
private void notifyInterfaceRemoved(String iface) {
// netd already clears out quota and alerts for removed ifaces; update
- // our sanity-checking state.
+ // our validity-checking state.
mActiveAlerts.remove(iface);
mActiveQuotas.remove(iface);
invokeForAllObservers(o -> o.interfaceRemoved(iface));
@@ -1256,7 +1256,7 @@
public void setInterfaceAlert(String iface, long alertBytes) {
NetworkStack.checkNetworkStackPermission(mContext);
- // quick sanity check
+ // quick validity check
if (!mActiveQuotas.containsKey(iface)) {
throw new IllegalStateException("setting alert requires existing quota on iface");
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index eb18da2..e342c1f 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1076,7 +1076,7 @@
}
try {
- // TODO(b/135341433): Remove paranoid logging when FUSE is stable
+ // TODO(b/135341433): Remove cautious logging when FUSE is stable
Slog.i(TAG, "Resetting vold...");
mVold.reset();
Slog.i(TAG, "Reset vold");
@@ -2173,7 +2173,7 @@
private void mount(VolumeInfo vol) {
try {
- // TODO(b/135341433): Remove paranoid logging when FUSE is stable
+ // TODO(b/135341433): Remove cautious logging when FUSE is stable
Slog.i(TAG, "Mounting volume " + vol);
mVold.mount(vol.id, vol.mountFlags, vol.mountUserId, new IVoldMountCallback.Stub() {
@Override
@@ -2904,7 +2904,7 @@
return 0;
}
- /** Set the password for encrypting the master key.
+ /** Set the password for encrypting the main key.
* @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager.
* @param password The password to set.
*/
@@ -2968,7 +2968,7 @@
}
/**
- * Get the type of encryption used to encrypt the master key.
+ * Get the type of encryption used to encrypt the main key.
* @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager.
*/
@Override
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index e433fbd..ba2e147 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1799,20 +1799,16 @@
}
@Override
- public void notifyCellLocationForSubscriber(int subId, CellIdentity cellLocation) {
+ public void notifyCellLocationForSubscriber(int subId, CellIdentity cellIdentity) {
log("notifyCellLocationForSubscriber: subId=" + subId
- + " cellLocation=" + cellLocation);
+ + " cellIdentity=" + cellIdentity);
if (!checkNotifyPermission("notifyCellLocation()")) {
return;
}
- if (VDBG) {
- log("notifyCellLocationForSubscriber: subId=" + subId
- + " cellLocation=" + cellLocation);
- }
int phoneId = getPhoneIdFromSubId(subId);
synchronized (mRecords) {
- if (validatePhoneId(phoneId)) {
- mCellIdentity[phoneId] = cellLocation;
+ if (validatePhoneId(phoneId) && !Objects.equals(cellIdentity, mCellIdentity[phoneId])) {
+ mCellIdentity[phoneId] = cellIdentity;
for (Record r : mRecords) {
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) &&
idMatch(r.subId, subId, phoneId) &&
@@ -1820,10 +1816,10 @@
&& checkFineLocationAccess(r, Build.VERSION_CODES.Q))) {
try {
if (DBG_LOC) {
- log("notifyCellLocation: cellLocation=" + cellLocation
+ log("notifyCellLocation: cellIdentity=" + cellIdentity
+ " r=" + r);
}
- r.callback.onCellLocationChanged(cellLocation);
+ r.callback.onCellLocationChanged(cellIdentity);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -2149,11 +2145,11 @@
if (validatePhoneId(phoneId)) {
mOutgoingSmsEmergencyNumber[phoneId] = emergencyNumber;
for (Record r : mRecords) {
+ // Send to all listeners regardless of subscription
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS)
- && idMatch(r.subId, subId, phoneId)) {
+ PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS)) {
try {
- r.callback.onOutgoingEmergencySms(emergencyNumber);
+ r.callback.onOutgoingEmergencySms(emergencyNumber, subId);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 7d81d41..5a34283 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -2326,7 +2326,7 @@
List<String> accountRemovedReceivers =
getAccountRemovedReceivers(account, accounts);
accounts.accountsDb.beginTransaction();
- // Set to a dummy value, this will only be used if the database
+ // Set to a placeholder value, this will only be used if the database
// transaction succeeds.
long accountId = -1;
try {
@@ -6284,7 +6284,7 @@
PRE_N_DATABASE_NAME);
if (userId == 0) {
// Migrate old file, if it exists, to the new location.
- // Make sure the new file doesn't already exist. A dummy file could have been
+ // Make sure the new file doesn't already exist. A placeholder file could have been
// accidentally created in the old location,
// causing the new one to become corrupted as well.
File oldFile = new File(systemDir, PRE_N_DATABASE_NAME);
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index fede1d2..48055b5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -26,6 +26,7 @@
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
+import android.os.Message;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.OnPropertiesChangedListener;
import android.provider.DeviceConfig.Properties;
@@ -124,6 +125,7 @@
private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000;
private static final int DEFAULT_PENDINGINTENT_WARNING_THRESHOLD = 2000;
private static final int DEFAULT_MIN_CRASH_INTERVAL = 2 * 60 * 1000;
+ private static final int DEFAULT_MAX_PHANTOM_PROCESSES = 32;
// Flag stored in the DeviceConfig API.
@@ -133,6 +135,11 @@
private static final String KEY_MAX_CACHED_PROCESSES = "max_cached_processes";
/**
+ * Maximum number of cached processes.
+ */
+ private static final String KEY_MAX_PHANTOM_PROCESSES = "max_phantom_processes";
+
+ /**
* Default value for mFlagBackgroundActivityStartsEnabled if not explicitly set in
* Settings.Global. This allows it to be set experimentally unless it has been
* enabled/disabled in developer options. Defaults to false.
@@ -364,6 +371,11 @@
*/
public final ArraySet<ComponentName> KEEP_WARMING_SERVICES = new ArraySet<ComponentName>();
+ /**
+ * Maximum number of phantom processes.
+ */
+ public int MAX_PHANTOM_PROCESSES = DEFAULT_MAX_PHANTOM_PROCESSES;
+
private List<String> mDefaultImperceptibleKillExemptPackages;
private List<Integer> mDefaultImperceptibleKillExemptProcStates;
@@ -481,6 +493,9 @@
case KEY_BINDER_HEAVY_HITTER_AUTO_SAMPLER_THRESHOLD:
updateBinderHeavyHitterWatcher();
break;
+ case KEY_MAX_PHANTOM_PROCESSES:
+ updateMaxPhantomProcesses();
+ break;
default:
break;
}
@@ -599,6 +614,8 @@
// with defaults.
Slog.e("ActivityManagerConstants", "Bad activity manager config settings", e);
}
+ final long currentPowerCheckInterval = POWER_CHECK_INTERVAL;
+
BACKGROUND_SETTLE_TIME = mParser.getLong(KEY_BACKGROUND_SETTLE_TIME,
DEFAULT_BACKGROUND_SETTLE_TIME);
FGSERVICE_MIN_SHOWN_TIME = mParser.getLong(KEY_FGSERVICE_MIN_SHOWN_TIME,
@@ -664,6 +681,13 @@
PENDINGINTENT_WARNING_THRESHOLD = mParser.getInt(KEY_PENDINGINTENT_WARNING_THRESHOLD,
DEFAULT_PENDINGINTENT_WARNING_THRESHOLD);
+ if (POWER_CHECK_INTERVAL != currentPowerCheckInterval) {
+ mService.mHandler.removeMessages(
+ ActivityManagerService.CHECK_EXCESSIVE_POWER_USE_MSG);
+ final Message msg = mService.mHandler.obtainMessage(
+ ActivityManagerService.CHECK_EXCESSIVE_POWER_USE_MSG);
+ mService.mHandler.sendMessageDelayed(msg, POWER_CHECK_INTERVAL);
+ }
// For new flags that are intended for server-side experiments, please use the new
// DeviceConfig package.
}
@@ -811,6 +835,16 @@
mService.scheduleUpdateBinderHeavyHitterWatcherConfig();
}
+ private void updateMaxPhantomProcesses() {
+ final int oldVal = MAX_PHANTOM_PROCESSES;
+ MAX_PHANTOM_PROCESSES = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_MAX_PHANTOM_PROCESSES,
+ DEFAULT_MAX_PHANTOM_PROCESSES);
+ if (oldVal > MAX_PHANTOM_PROCESSES) {
+ mService.mHandler.post(mService.mPhantomProcessList::trimPhantomProcessesIfNecessary);
+ }
+ }
+
void dump(PrintWriter pw) {
pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) "
+ Settings.Global.ACTIVITY_MANAGER_CONSTANTS + ":");
@@ -897,6 +931,8 @@
pw.println(BINDER_HEAVY_HITTER_AUTO_SAMPLER_BATCHSIZE);
pw.print(" "); pw.print(KEY_BINDER_HEAVY_HITTER_AUTO_SAMPLER_THRESHOLD); pw.print("=");
pw.println(BINDER_HEAVY_HITTER_AUTO_SAMPLER_THRESHOLD);
+ pw.print(" "); pw.print(KEY_MAX_PHANTOM_PROCESSES); pw.print("=");
+ pw.println(MAX_PHANTOM_PROCESSES);
pw.println();
if (mOverrideMaxCachedProcesses >= 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b55d555..b1b4018 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -635,6 +635,12 @@
final ProcessList mProcessList;
/**
+ * The list of phantom processes.
+ * @see PhantomProcessRecord
+ */
+ final PhantomProcessList mPhantomProcessList;
+
+ /**
* Tracking long-term execution of processes to look for abuse and other
* bad app behavior.
*/
@@ -1996,6 +2002,7 @@
mProcessList = injector.getProcessList(this);
mProcessList.init(this, activeUids, mPlatformCompat);
mAppProfiler = new AppProfiler(this, BackgroundThread.getHandler().getLooper(), null);
+ mPhantomProcessList = new PhantomProcessList(this);
mOomAdjuster = hasHandlerThread
? new OomAdjuster(this, mProcessList, activeUids, handlerThread) : null;
@@ -2053,6 +2060,7 @@
mProcessList.init(this, activeUids, mPlatformCompat);
mAppProfiler = new AppProfiler(this, BackgroundThread.getHandler().getLooper(),
new LowMemDetector(this));
+ mPhantomProcessList = new PhantomProcessList(this);
mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
// Broadcast policy parameters
@@ -9209,6 +9217,10 @@
}
}
+ if (dumpAll) {
+ mPhantomProcessList.dump(pw, " ");
+ }
+
if (mImportantProcesses.size() > 0) {
synchronized (mPidsSelfLocked) {
boolean printed = false;
@@ -14832,44 +14844,24 @@
int i = mProcessList.mLruProcesses.size();
while (i > 0) {
i--;
- ProcessRecord app = mProcessList.mLruProcesses.get(i);
+ final ProcessRecord app = mProcessList.mLruProcesses.get(i);
if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
- if (app.lastCpuTime <= 0) {
- continue;
+ int cpuLimit;
+ long checkDur = curUptime - app.getWhenUnimportant();
+ if (checkDur <= mConstants.POWER_CHECK_INTERVAL) {
+ cpuLimit = mConstants.POWER_CHECK_MAX_CPU_1;
+ } else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL * 2)
+ || app.setProcState <= ActivityManager.PROCESS_STATE_HOME) {
+ cpuLimit = mConstants.POWER_CHECK_MAX_CPU_2;
+ } else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL * 3)) {
+ cpuLimit = mConstants.POWER_CHECK_MAX_CPU_3;
+ } else {
+ cpuLimit = mConstants.POWER_CHECK_MAX_CPU_4;
}
- long cputimeUsed = app.curCpuTime - app.lastCpuTime;
- if (DEBUG_POWER) {
- StringBuilder sb = new StringBuilder(128);
- sb.append("CPU for ");
- app.toShortString(sb);
- sb.append(": over ");
- TimeUtils.formatDuration(uptimeSince, sb);
- sb.append(" used ");
- TimeUtils.formatDuration(cputimeUsed, sb);
- sb.append(" (");
- sb.append((cputimeUsed * 100) / uptimeSince);
- sb.append("%)");
- Slog.i(TAG_POWER, sb.toString());
- }
- // If the process has used too much CPU over the last duration, the
- // user probably doesn't want this, so kill!
- if (doCpuKills && uptimeSince > 0) {
- // What is the limit for this process?
- int cpuLimit;
- long checkDur = curUptime - app.getWhenUnimportant();
- if (checkDur <= mConstants.POWER_CHECK_INTERVAL) {
- cpuLimit = mConstants.POWER_CHECK_MAX_CPU_1;
- } else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL * 2)
- || app.setProcState <= ActivityManager.PROCESS_STATE_HOME) {
- cpuLimit = mConstants.POWER_CHECK_MAX_CPU_2;
- } else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL * 3)) {
- cpuLimit = mConstants.POWER_CHECK_MAX_CPU_3;
- } else {
- cpuLimit = mConstants.POWER_CHECK_MAX_CPU_4;
- }
- if (((cputimeUsed * 100) / uptimeSince) >= cpuLimit) {
- mBatteryStatsService.reportExcessiveCpu(app.info.uid, app.processName,
- uptimeSince, cputimeUsed);
+ if (app.lastCpuTime > 0) {
+ final long cputimeUsed = app.curCpuTime - app.lastCpuTime;
+ if (checkExcessivePowerUsageLocked(uptimeSince, doCpuKills, cputimeUsed,
+ app.processName, app.toShortString(), cpuLimit, app)) {
app.kill("excessive cpu " + cputimeUsed + " during " + uptimeSince
+ " dur=" + checkDur + " limit=" + cpuLimit,
ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE,
@@ -14878,23 +14870,73 @@
synchronized (mProcessStats.mLock) {
app.baseProcessTracker.reportExcessiveCpu(app.pkgList.mPkgList);
}
- for (int ipkg = app.pkgList.size() - 1; ipkg >= 0; ipkg--) {
- ProcessStats.ProcessStateHolder holder = app.pkgList.valueAt(ipkg);
- FrameworkStatsLog.write(
- FrameworkStatsLog.EXCESSIVE_CPU_USAGE_REPORTED,
- app.info.uid,
- holder.state.getName(),
- holder.state.getPackage(),
- holder.appVersion);
- }
}
}
+
app.lastCpuTime = app.curCpuTime;
+
+ // Also check the phantom processes if there is any
+ final long chkDur = checkDur;
+ final int cpuLmt = cpuLimit;
+ final boolean doKill = doCpuKills;
+ mPhantomProcessList.forEachPhantomProcessOfApp(app, r -> {
+ if (r.mLastCputime > 0) {
+ final long cputimeUsed = r.mCurrentCputime - r.mLastCputime;
+ if (checkExcessivePowerUsageLocked(uptimeSince, doKill, cputimeUsed,
+ app.processName, r.toString(), cpuLimit, app)) {
+ mPhantomProcessList.killPhantomProcessGroupLocked(app, r,
+ ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE,
+ ApplicationExitInfo.SUBREASON_EXCESSIVE_CPU,
+ "excessive cpu " + cputimeUsed + " during "
+ + uptimeSince + " dur=" + chkDur + " limit=" + cpuLmt);
+ return false;
+ }
+ }
+ r.mLastCputime = r.mCurrentCputime;
+ return true;
+ });
}
}
}
}
+ private boolean checkExcessivePowerUsageLocked(final long uptimeSince, boolean doCpuKills,
+ final long cputimeUsed, final String processName, final String description,
+ final int cpuLimit, final ProcessRecord app) {
+ if (DEBUG_POWER) {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("CPU for ");
+ sb.append(description);
+ sb.append(": over ");
+ TimeUtils.formatDuration(uptimeSince, sb);
+ sb.append(" used ");
+ TimeUtils.formatDuration(cputimeUsed, sb);
+ sb.append(" (");
+ sb.append((cputimeUsed * 100.0) / uptimeSince);
+ sb.append("%)");
+ Slog.i(TAG_POWER, sb.toString());
+ }
+ // If the process has used too much CPU over the last duration, the
+ // user probably doesn't want this, so kill!
+ if (doCpuKills && uptimeSince > 0) {
+ if (((cputimeUsed * 100) / uptimeSince) >= cpuLimit) {
+ mBatteryStatsService.reportExcessiveCpu(app.info.uid, app.processName,
+ uptimeSince, cputimeUsed);
+ for (int ipkg = app.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+ ProcessStats.ProcessStateHolder holder = app.pkgList.valueAt(ipkg);
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.EXCESSIVE_CPU_USAGE_REPORTED,
+ app.info.uid,
+ processName,
+ holder.state.getPackage(),
+ holder.appVersion);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
synchronized (mProcessStats.mLock) {
if (proc.thread != null && proc.baseProcessTracker != null) {
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 0b5d585..31ffb35 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1257,6 +1257,10 @@
}
}
+ if (haveNewCpuStats) {
+ mService.mPhantomProcessList.updateProcessCpuStatesLocked(mProcessCpuTracker);
+ }
+
final BatteryStatsImpl bstats = mService.mBatteryStatsService.getActiveStatistics();
synchronized (bstats) {
if (haveNewCpuStats) {
diff --git a/services/core/java/com/android/server/am/BugReportHandlerUtil.java b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
index 0a0d8d8..2142ebc 100644
--- a/services/core/java/com/android/server/am/BugReportHandlerUtil.java
+++ b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
@@ -63,7 +63,7 @@
}
/**
- * Launches a bugreport-whitelisted app to handle a bugreport.
+ * Launches a bugreport-allowlisted app to handle a bugreport.
*
* <p>Allows a bug report handler app to take bugreports on the user's behalf. The handler can
* be predefined in the config, meant to be launched with the primary user. The user can
@@ -71,7 +71,7 @@
* useful for capturing bug reports from work profile, for instance.
*
* @param context Context
- * @return true if there is a bugreport-whitelisted app to handle a bugreport, or false
+ * @return true if there is a bugreport-allowlisted app to handle a bugreport, or false
* otherwise
*/
static boolean launchBugReportHandlerApp(Context context) {
@@ -92,7 +92,7 @@
// It looks like the settings are outdated, reset outdated settings.
//
// i.e.
- // If user chooses which profile and which bugreport-whitelisted app in that
+ // If user chooses which profile and which bugreport-allowlisted app in that
// profile to handle a bugreport, then user remove the profile.
// === RESULT ===
// The chosen bugreport handler app is outdated because the profile is removed,
@@ -184,7 +184,7 @@
}
private static boolean isBugreportWhitelistedApp(String app) {
- // Verify the app is bugreport-whitelisted
+ // Verify the app is bugreport-allowlisted
final ArraySet<String> whitelistedApps = SystemConfig.getInstance()
.getBugreportWhitelistedPackages();
return whitelistedApps.contains(app);
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 9660389..8112bb8 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -22,6 +22,7 @@
import android.app.ActivityManager;
import android.app.ActivityThread;
+import android.app.ApplicationExitInfo;
import android.os.Debug;
import android.os.Handler;
import android.os.Message;
@@ -132,11 +133,15 @@
static final int REPORT_UNFREEZE_MSG = 4;
//TODO:change this static definition into a configurable flag.
- static final int FREEZE_TIMEOUT_MS = 500;
+ static final int FREEZE_TIMEOUT_MS = 10000;
static final int DO_FREEZE = 1;
static final int REPORT_UNFREEZE = 2;
+ // Bitfield values for sync/async transactions reveived by frozen processes
+ static final int SYNC_RECEIVED_WHILE_FROZEN = 1;
+ static final int ASYNC_RECEIVED_WHILE_FROZEN = 2;
+
/**
* This thread must be moved to the system background cpuset.
* If that doesn't happen, it's probably going to draw a lot of power.
@@ -494,6 +499,15 @@
private static native void freezeBinder(int pid, boolean freeze);
/**
+ * Retrieves binder freeze info about a process.
+ * @param pid the pid for which binder freeze info is to be retrieved.
+ *
+ * @throws RuntimeException if the operation could not complete successfully.
+ * @return a bit field reporting the binder freeze info for the process.
+ */
+ private static native int getBinderFreezeInfo(int pid);
+
+ /**
* Determines whether the freezer is supported by this system
*/
public static boolean isFreezerSupported() {
@@ -729,6 +743,37 @@
return;
}
+ boolean processKilled = false;
+
+ try {
+ int freezeInfo = getBinderFreezeInfo(app.pid);
+
+ if ((freezeInfo & SYNC_RECEIVED_WHILE_FROZEN) != 0) {
+ Slog.d(TAG_AM, "pid " + app.pid + " " + app.processName + " "
+ + " received sync transactions while frozen, killing");
+ app.kill("Sync transaction while in frozen state",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
+ processKilled = true;
+ }
+
+ if ((freezeInfo & ASYNC_RECEIVED_WHILE_FROZEN) != 0) {
+ Slog.d(TAG_AM, "pid " + app.pid + " " + app.processName + " "
+ + " received async transactions while frozen");
+ }
+ } catch (Exception e) {
+ Slog.d(TAG_AM, "Unable to query binder frozen info for pid " + app.pid + " "
+ + app.processName + ". Killing it. Exception: " + e);
+ app.kill("Unable to query binder frozen stats",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
+ processKilled = true;
+ }
+
+ if (processKilled) {
+ return;
+ }
+
long freezeTime = app.freezeUnfreezeTime;
try {
@@ -745,8 +790,12 @@
try {
freezeBinder(app.pid, false);
} catch (RuntimeException e) {
- // TODO: it might be preferable to kill the target pid in this case
- Slog.e(TAG_AM, "Unable to unfreeze binder for " + app.pid + " " + app.processName);
+ 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) {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index c313eb5..1bf62a0 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1113,8 +1113,8 @@
+ " to " + uidRec.curWhitelist);
if (ActivityManager.isProcStateBackground(uidRec.getCurProcState())
&& !uidRec.curWhitelist) {
- // UID is now in the background (and not on the temp whitelist). Was it
- // previously in the foreground (or on the temp whitelist)?
+ // UID is now in the background (and not on the temp allowlist). Was it
+ // previously in the foreground (or on the temp allowlist)?
if (!ActivityManager.isProcStateBackground(uidRec.setProcState)
|| uidRec.setWhitelist) {
uidRec.lastBackgroundTime = nowElapsed;
diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java
new file mode 100644
index 0000000..e2fcf08
--- /dev/null
+++ b/services/core/java/com/android/server/am/PhantomProcessList.java
@@ -0,0 +1,395 @@
+/*
+ * 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.am;
+
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
+
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.app.ApplicationExitInfo.Reason;
+import android.app.ApplicationExitInfo.SubReason;
+import android.os.Handler;
+import android.os.Process;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.ProcessCpuTracker;
+
+import libcore.io.IoUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.function.Function;
+
+/**
+ * Activity manager code dealing with phantom processes.
+ */
+public final class PhantomProcessList {
+ static final String TAG = TAG_WITH_CLASS_NAME ? "PhantomProcessList" : TAG_AM;
+
+ final Object mLock = new Object();
+
+ /**
+ * All of the phantom process record we track, key is the pid of the process.
+ */
+ @GuardedBy("mLock")
+ final SparseArray<PhantomProcessRecord> mPhantomProcesses = new SparseArray<>();
+
+ /**
+ * The mapping between app processes and their phantom processess, outer key is the pid of
+ * the app process, while the inner key is the pid of the phantom process.
+ */
+ @GuardedBy("mLock")
+ final SparseArray<SparseArray<PhantomProcessRecord>> mAppPhantomProcessMap =
+ new SparseArray<>();
+
+ /**
+ * The mapping of the pidfd to PhantomProcessRecord.
+ */
+ @GuardedBy("mLock")
+ final SparseArray<PhantomProcessRecord> mPhantomProcessesPidFds = new SparseArray<>();
+
+ /**
+ * The list of phantom processes tha's being signaled to be killed but still undead yet.
+ */
+ @GuardedBy("mLock")
+ final SparseArray<PhantomProcessRecord> mZombiePhantomProcesses = new SparseArray<>();
+
+ @GuardedBy("mLock")
+ private final ArrayList<PhantomProcessRecord> mTempPhantomProcesses = new ArrayList<>();
+
+ @GuardedBy("mLock")
+ private boolean mTrimPhantomProcessScheduled = false;
+
+ @GuardedBy("mLock")
+ int mUpdateSeq;
+
+ private final ActivityManagerService mService;
+ private final Handler mKillHandler;
+
+ PhantomProcessList(final ActivityManagerService service) {
+ mService = service;
+ mKillHandler = service.mProcessList.sKillHandler;
+ }
+
+ /**
+ * Get the existing phantom process record, or create if it's not existing yet;
+ * however, before creating it, we'll check if this is really a phantom process
+ * and we'll return null if it's not.
+ */
+ @GuardedBy("mLock")
+ PhantomProcessRecord getOrCreatePhantomProcessIfNeededLocked(final String processName,
+ final int uid, final int pid) {
+ // First check if it's actually an app process we know
+ if (isAppProcess(pid)) {
+ return null;
+ }
+
+ // Have we already been aware of this?
+ final int index = mPhantomProcesses.indexOfKey(pid);
+ if (index >= 0) {
+ final PhantomProcessRecord proc = mPhantomProcesses.valueAt(index);
+ if (proc.equals(processName, uid, pid)) {
+ return proc;
+ }
+ // Somehow our record doesn't match, remove it anyway
+ Slog.w(TAG, "Stale " + proc + ", removing");
+ mPhantomProcesses.removeAt(index);
+ } else {
+ // Is this one of the zombie processes we've known?
+ final int idx = mZombiePhantomProcesses.indexOfKey(pid);
+ if (idx >= 0) {
+ final PhantomProcessRecord proc = mZombiePhantomProcesses.valueAt(idx);
+ if (proc.equals(processName, uid, pid)) {
+ return proc;
+ }
+ // Our zombie process information is outdated, let's remove this one, it shoud
+ // have been gone.
+ mZombiePhantomProcesses.removeAt(idx);
+ }
+ }
+
+ int ppid = getParentPid(pid);
+
+ // Walk through its parents and see if it could be traced back to an app process.
+ while (ppid > 1) {
+ if (isAppProcess(ppid)) {
+ // It's a phantom process, bookkeep it
+ try {
+ final PhantomProcessRecord proc = new PhantomProcessRecord(
+ processName, uid, pid, ppid, mService,
+ this::onPhantomProcessKilledLocked);
+ proc.mUpdateSeq = mUpdateSeq;
+ mPhantomProcesses.put(pid, proc);
+ SparseArray<PhantomProcessRecord> array = mAppPhantomProcessMap.get(ppid);
+ if (array == null) {
+ array = new SparseArray<>();
+ mAppPhantomProcessMap.put(ppid, array);
+ }
+ array.put(pid, proc);
+ if (proc.mPidFd != null) {
+ mKillHandler.getLooper().getQueue().addOnFileDescriptorEventListener(
+ proc.mPidFd, EVENT_INPUT | EVENT_ERROR,
+ this::onPhantomProcessFdEvent);
+ mPhantomProcessesPidFds.put(proc.mPidFd.getInt$(), proc);
+ }
+ scheduleTrimPhantomProcessesLocked();
+ return proc;
+ } catch (IllegalStateException e) {
+ return null;
+ }
+ }
+
+ ppid = getParentPid(ppid);
+ }
+ return null;
+ }
+
+ private static int getParentPid(int pid) {
+ try {
+ return Process.getParentPid(pid);
+ } catch (Exception e) {
+ }
+ return -1;
+ }
+
+ private boolean isAppProcess(int pid) {
+ synchronized (mService.mPidsSelfLocked) {
+ return mService.mPidsSelfLocked.get(pid) != null;
+ }
+ }
+
+ private int onPhantomProcessFdEvent(FileDescriptor fd, int events) {
+ synchronized (mLock) {
+ final PhantomProcessRecord proc = mPhantomProcessesPidFds.get(fd.getInt$());
+ if ((events & EVENT_INPUT) != 0) {
+ proc.onProcDied(true);
+ } else {
+ // EVENT_ERROR, kill the process
+ proc.killLocked("Process error", true);
+ }
+ }
+ return 0;
+ }
+
+ @GuardedBy("mLock")
+ private void onPhantomProcessKilledLocked(final PhantomProcessRecord proc) {
+ if (proc.mPidFd != null && proc.mPidFd.valid()) {
+ mKillHandler.getLooper().getQueue()
+ .removeOnFileDescriptorEventListener(proc.mPidFd);
+ mPhantomProcessesPidFds.remove(proc.mPidFd.getInt$());
+ IoUtils.closeQuietly(proc.mPidFd);
+ }
+ mPhantomProcesses.remove(proc.mPid);
+ final int index = mAppPhantomProcessMap.indexOfKey(proc.mPpid);
+ if (index < 0) {
+ return;
+ }
+ SparseArray<PhantomProcessRecord> array = mAppPhantomProcessMap.valueAt(index);
+ array.remove(proc.mPid);
+ if (array.size() == 0) {
+ mAppPhantomProcessMap.removeAt(index);
+ }
+ if (proc.mZombie) {
+ // If it's not really dead, bookkeep it
+ mZombiePhantomProcesses.put(proc.mPid, proc);
+ } else {
+ // In case of race condition, let's try to remove it from zombie list
+ mZombiePhantomProcesses.remove(proc.mPid);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void scheduleTrimPhantomProcessesLocked() {
+ if (!mTrimPhantomProcessScheduled) {
+ mTrimPhantomProcessScheduled = true;
+ mService.mHandler.post(this::trimPhantomProcessesIfNecessary);
+ }
+ }
+
+ /**
+ * Clamp the number of phantom processes to
+ * {@link ActivityManagerConstants#MAX_PHANTOM_PROCESSE}, kills those surpluses in the
+ * order of the oom adjs of their parent process.
+ */
+ void trimPhantomProcessesIfNecessary() {
+ synchronized (mLock) {
+ mTrimPhantomProcessScheduled = false;
+ if (mService.mConstants.MAX_PHANTOM_PROCESSES < mPhantomProcesses.size()) {
+ for (int i = mPhantomProcesses.size() - 1; i >= 0; i--) {
+ mTempPhantomProcesses.add(mPhantomProcesses.valueAt(i));
+ }
+ synchronized (mService.mPidsSelfLocked) {
+ Collections.sort(mTempPhantomProcesses, (a, b) -> {
+ final ProcessRecord ra = mService.mPidsSelfLocked.get(a.mPpid);
+ if (ra == null) {
+ // parent is gone, this process should have been killed too
+ return 1;
+ }
+ final ProcessRecord rb = mService.mPidsSelfLocked.get(b.mPpid);
+ if (rb == null) {
+ // parent is gone, this process should have been killed too
+ return -1;
+ }
+ if (ra.curAdj != rb.curAdj) {
+ return ra.curAdj - rb.curAdj;
+ }
+ if (a.mKnownSince != b.mKnownSince) {
+ // In case of identical oom adj, younger one first
+ return a.mKnownSince < b.mKnownSince ? 1 : -1;
+ }
+ return 0;
+ });
+ }
+ for (int i = mTempPhantomProcesses.size() - 1;
+ i >= mService.mConstants.MAX_PHANTOM_PROCESSES; i--) {
+ final PhantomProcessRecord proc = mTempPhantomProcesses.get(i);
+ proc.killLocked("Trimming phantom processes", true);
+ }
+ mTempPhantomProcesses.clear();
+ }
+ }
+ }
+
+ /**
+ * Remove all entries with outdated seq num.
+ */
+ @GuardedBy("mLock")
+ void pruneStaleProcessesLocked() {
+ for (int i = mPhantomProcesses.size() - 1; i >= 0; i--) {
+ final PhantomProcessRecord proc = mPhantomProcesses.valueAt(i);
+ if (proc.mUpdateSeq < mUpdateSeq) {
+ if (DEBUG_PROCESSES) {
+ Slog.v(TAG, "Pruning " + proc + " as it should have been dead.");
+ }
+ proc.killLocked("Stale process", true);
+ }
+ }
+ for (int i = mZombiePhantomProcesses.size() - 1; i >= 0; i--) {
+ final PhantomProcessRecord proc = mZombiePhantomProcesses.valueAt(i);
+ if (proc.mUpdateSeq < mUpdateSeq) {
+ if (DEBUG_PROCESSES) {
+ Slog.v(TAG, "Pruning " + proc + " as it should have been dead.");
+ }
+ }
+ }
+ }
+
+ /**
+ * Kill the given phantom process, all its siblings (if any) and their parent process
+ */
+ @GuardedBy("mService")
+ void killPhantomProcessGroupLocked(ProcessRecord app, PhantomProcessRecord proc,
+ @Reason int reasonCode, @SubReason int subReason, String msg) {
+ synchronized (mLock) {
+ int index = mAppPhantomProcessMap.indexOfKey(proc.mPpid);
+ if (index >= 0) {
+ final SparseArray<PhantomProcessRecord> array =
+ mAppPhantomProcessMap.valueAt(index);
+ for (int i = array.size() - 1; i >= 0; i--) {
+ final PhantomProcessRecord r = array.valueAt(i);
+ if (r == proc) {
+ r.killLocked(msg, true);
+ } else {
+ r.killLocked("Caused by siling process: " + msg, false);
+ }
+ }
+ }
+ }
+ // Lastly, kill the parent process too
+ app.kill("Caused by child process: " + msg, reasonCode, subReason, true);
+ }
+
+ /**
+ * Iterate all phantom process belonging to the given app, and invokve callback
+ * for each of them.
+ */
+ void forEachPhantomProcessOfApp(final ProcessRecord app,
+ final Function<PhantomProcessRecord, Boolean> callback) {
+ synchronized (mLock) {
+ int index = mAppPhantomProcessMap.indexOfKey(app.pid);
+ if (index >= 0) {
+ final SparseArray<PhantomProcessRecord> array =
+ mAppPhantomProcessMap.valueAt(index);
+ for (int i = array.size() - 1; i >= 0; i--) {
+ final PhantomProcessRecord r = array.valueAt(i);
+ if (!callback.apply(r)) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ @GuardedBy("tracker")
+ void updateProcessCpuStatesLocked(ProcessCpuTracker tracker) {
+ synchronized (mLock) {
+ // refresh the phantom process list with the latest cpu stats results.
+ mUpdateSeq++;
+ for (int i = tracker.countStats() - 1; i >= 0; i--) {
+ final ProcessCpuTracker.Stats st = tracker.getStats(i);
+ final PhantomProcessRecord r =
+ getOrCreatePhantomProcessIfNeededLocked(st.name, st.uid, st.pid);
+ if (r != null) {
+ r.mUpdateSeq = mUpdateSeq;
+ r.mCurrentCputime += st.rel_utime + st.rel_stime;
+ if (r.mLastCputime == 0) {
+ r.mLastCputime = r.mCurrentCputime;
+ }
+ r.updateAdjLocked();
+ }
+ }
+ // remove the stale ones
+ pruneStaleProcessesLocked();
+ }
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ synchronized (mLock) {
+ dumpPhantomeProcessLocked(pw, prefix, "All Active App Child Processes:",
+ mPhantomProcesses);
+ dumpPhantomeProcessLocked(pw, prefix, "All Zombie App Child Processes:",
+ mZombiePhantomProcesses);
+ }
+ }
+
+ void dumpPhantomeProcessLocked(PrintWriter pw, String prefix, String headline,
+ SparseArray<PhantomProcessRecord> list) {
+ final int size = list.size();
+ if (size == 0) {
+ return;
+ }
+ pw.println();
+ pw.print(prefix);
+ pw.println(headline);
+ for (int i = 0; i < size; i++) {
+ final PhantomProcessRecord proc = list.valueAt(i);
+ pw.print(prefix);
+ pw.print(" proc #");
+ pw.print(i);
+ pw.print(": ");
+ pw.println(proc.toString());
+ proc.dump(pw, prefix + " ");
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/PhantomProcessRecord.java b/services/core/java/com/android/server/am/PhantomProcessRecord.java
new file mode 100644
index 0000000..0156ee5
--- /dev/null
+++ b/services/core/java/com/android/server/am/PhantomProcessRecord.java
@@ -0,0 +1,237 @@
+/*
+ * 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.am;
+
+import static android.os.Process.PROC_NEWLINE_TERM;
+import static android.os.Process.PROC_OUT_LONG;
+
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.os.Handler;
+import android.os.Process;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.EventLog;
+import android.util.Slog;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.function.Consumer;
+
+/**
+ * The "phantom" app processes, which are forked by app processes so we are not aware of
+ * them until we walk through the process list in /proc.
+ */
+public final class PhantomProcessRecord {
+ static final String TAG = TAG_WITH_CLASS_NAME ? "PhantomProcessRecord" : TAG_AM;
+
+ static final long[] LONG_OUT = new long[1];
+ static final int[] LONG_FORMAT = new int[] {PROC_NEWLINE_TERM | PROC_OUT_LONG};
+
+ final String mProcessName; // name of the process
+ final int mUid; // uid of the process
+ final int mPid; // The id of the process
+ final int mPpid; // Ancestor (managed app process) pid of the process
+ final long mKnownSince; // The timestamp when we're aware of the process
+ final FileDescriptor mPidFd; // The fd to monitor the termination of this process
+
+ long mLastCputime; // How long proc has run CPU at last check
+ long mCurrentCputime; // How long proc has run CPU most recently
+ int mUpdateSeq; // Seq no, indicating the last check on this process
+ int mAdj; // The last known oom adj score
+ boolean mKilled; // Whether it has been killed by us or not
+ boolean mZombie; // Whether it was signaled to be killed but timed out
+ String mStringName; // Caching of the toString() result
+
+ final ActivityManagerService mService;
+ final Object mLock;
+ final Consumer<PhantomProcessRecord> mOnKillListener;
+ final Handler mKillHandler;
+
+ PhantomProcessRecord(final String processName, final int uid, final int pid,
+ final int ppid, final ActivityManagerService service,
+ final Consumer<PhantomProcessRecord> onKillListener) throws IllegalStateException {
+ mProcessName = processName;
+ mUid = uid;
+ mPid = pid;
+ mPpid = ppid;
+ mKilled = false;
+ mAdj = ProcessList.NATIVE_ADJ;
+ mKnownSince = SystemClock.elapsedRealtime();
+ mService = service;
+ mLock = service.mPhantomProcessList.mLock;
+ mOnKillListener = onKillListener;
+ mKillHandler = service.mProcessList.sKillHandler;
+ if (Process.supportsPidFd()) {
+ StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+ try {
+ mPidFd = Process.openPidFd(pid, 0);
+ if (mPidFd == null) {
+ throw new IllegalStateException();
+ }
+ } catch (IOException e) {
+ // Maybe a race condition, the process is gone.
+ Slog.w(TAG, "Unable to open process " + pid + ", it might be gone");
+ IllegalStateException ex = new IllegalStateException();
+ ex.initCause(e);
+ throw ex;
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
+ } else {
+ mPidFd = null;
+ }
+ }
+
+ @GuardedBy("mLock")
+ void killLocked(String reason, boolean noisy) {
+ if (!mKilled) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "kill");
+ if (noisy || mUid == mService.mCurOomAdjUid) {
+ mService.reportUidInfoMessageLocked(TAG,
+ "Killing " + toString() + ": " + reason, mUid);
+ }
+ if (mPid > 0) {
+ EventLog.writeEvent(EventLogTags.AM_KILL, UserHandle.getUserId(mUid),
+ mPid, mProcessName, mAdj, reason);
+ if (!Process.supportsPidFd()) {
+ onProcDied(false);
+ } else {
+ // We'll notify the listener when we're notified it's dead.
+ // Meanwhile, we'd also need handle the case of zombie processes.
+ mKillHandler.postDelayed(mProcKillTimer, this,
+ ProcessList.PROC_KILL_TIMEOUT);
+ }
+ Process.killProcessQuiet(mPid);
+ ProcessList.killProcessGroup(mUid, mPid);
+ }
+ mKilled = true;
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+ }
+
+ private Runnable mProcKillTimer = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mLock) {
+ // The process is maybe in either D or Z state.
+ Slog.w(TAG, "Process " + toString() + " is still alive after "
+ + ProcessList.PROC_KILL_TIMEOUT + "ms");
+ // Force a cleanup as we can't keep the fd open forever
+ mZombie = true;
+ onProcDied(false);
+ // But still bookkeep it, so it won't be added as a new one if it's spotted again.
+ }
+ }
+ };
+
+ @GuardedBy("mLock")
+ void updateAdjLocked() {
+ if (Process.readProcFile("/proc/" + mPid + "/oom_score_adj",
+ LONG_FORMAT, null, LONG_OUT, null)) {
+ mAdj = (int) LONG_OUT[0];
+ }
+ }
+
+ @GuardedBy("mLock")
+ void onProcDied(boolean reallyDead) {
+ if (reallyDead) {
+ Slog.i(TAG, "Process " + toString() + " died");
+ }
+ mKillHandler.removeCallbacks(mProcKillTimer, this);
+ if (mOnKillListener != null) {
+ mOnKillListener.accept(this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ if (mStringName != null) {
+ return mStringName;
+ }
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("PhantomProcessRecord {");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(' ');
+ sb.append(mPid);
+ sb.append(':');
+ sb.append(mPpid);
+ sb.append(':');
+ sb.append(mProcessName);
+ sb.append('/');
+ if (mUid < Process.FIRST_APPLICATION_UID) {
+ sb.append(mUid);
+ } else {
+ sb.append('u');
+ sb.append(UserHandle.getUserId(mUid));
+ int appId = UserHandle.getAppId(mUid);
+ if (appId >= Process.FIRST_APPLICATION_UID) {
+ sb.append('a');
+ sb.append(appId - Process.FIRST_APPLICATION_UID);
+ } else {
+ sb.append('s');
+ sb.append(appId);
+ }
+ if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) {
+ sb.append('i');
+ sb.append(appId - Process.FIRST_ISOLATED_UID);
+ }
+ }
+ sb.append('}');
+ return mStringName = sb.toString();
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ final long now = SystemClock.elapsedRealtime();
+ pw.print(prefix);
+ pw.print("user #");
+ pw.print(UserHandle.getUserId(mUid));
+ pw.print(" uid=");
+ pw.print(mUid);
+ pw.print(" pid=");
+ pw.print(mPid);
+ pw.print(" ppid=");
+ pw.print(mPpid);
+ pw.print(" knownSince=");
+ TimeUtils.formatDuration(mKnownSince, now, pw);
+ pw.print(" killed=");
+ pw.println(mKilled);
+ pw.print(prefix);
+ pw.print("lastCpuTime=");
+ pw.print(mLastCputime);
+ if (mLastCputime > 0) {
+ pw.print(" timeUsed=");
+ TimeUtils.formatDuration(mCurrentCputime - mLastCputime, pw);
+ }
+ pw.print(" oom adj=");
+ pw.print(mAdj);
+ pw.print(" seq=");
+ pw.println(mUpdateSeq);
+ }
+
+ boolean equals(final String processName, final int uid, final int pid) {
+ return mUid == uid && mPid == pid && TextUtils.equals(mProcessName, processName);
+ }
+}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index ced2f0f..5e65563 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -329,7 +329,7 @@
/**
* How long between a process kill and we actually receive its death recipient
*/
- private static final int PROC_KILL_TIMEOUT = 2000; // 2 seconds;
+ static final int PROC_KILL_TIMEOUT = 2000; // 2 seconds;
/**
* Native heap allocations will now have a non-zero tag in the most significant byte.
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index bac7565..60e59e3 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -237,7 +237,7 @@
SystemProperties.set(key, value);
} catch (Exception e) {
// Failure to set a property can be caused by SELinux denial. This usually indicates
- // that the property wasn't whitelisted in sepolicy.
+ // that the property wasn't allowlisted in sepolicy.
// No need to report it on all user devices, only on debug builds.
log("Unable to set property " + key + " value '" + value + "'", e);
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 83bf28e..eb60573 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -423,7 +423,7 @@
for (Integer userId : mUserLru) {
UserState uss = mStartedUsers.get(userId);
if (uss == null) {
- // Shouldn't happen, but be sane if it does.
+ // Shouldn't happen, but recover if it does.
continue;
}
if (uss.state == UserState.STATE_STOPPING
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 06f44b1..a80111c 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -6247,7 +6247,7 @@
int[] users;
if (userId == UserHandle.USER_ALL) {
- // TODO(b/157921703): this call is returning all users, not just live ones - we
+ // TODO(b/162888972): this call is returning all users, not just live ones - we
// need to either fix the method called, or rename the variable
List<UserInfo> liveUsers = UserManager.get(mContext).getUsers();
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f63c2ee..6110759 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1776,7 +1776,7 @@
Settings.Global.getInt(
cr, Settings.Global.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
int ringerMode = ringerModeFromSettings;
- // sanity check in case the settings are restored from a device with incompatible
+ // validity check in case the settings are restored from a device with incompatible
// ringer modes
if (!isValidRingerMode(ringerMode)) {
ringerMode = AudioManager.RINGER_MODE_NORMAL;
@@ -3397,7 +3397,7 @@
// For automotive,
// - the car service is always running as system user
// - foreground users are non-system users
- // Car service is in charge of dispatching the key event include master mute to Android.
+ // Car service is in charge of dispatching the key event include global mute to Android.
// Therefore, the getCurrentUser() is always different to the foreground user.
if ((isPlatformAutomotive() && userId == UserHandle.USER_SYSTEM)
|| (getCurrentUserId() == userId)) {
@@ -3409,7 +3409,7 @@
}
}
- /** get master mute state. */
+ /** get global mute state. */
public boolean isMasterMute() {
return AudioSystem.getMasterMute();
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
index cbfa87f..726cdc3 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
@@ -170,7 +170,7 @@
int len = config.ranges.size();
List<RadioManager.BandDescriptor> bands = new ArrayList<>(len);
- // Just a dummy value.
+ // Just a placeholder value.
int region = RadioManager.REGION_ITU_1;
for (AmFmBandRange range : config.ranges) {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 7175489..99dc58e 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -256,8 +256,8 @@
@GuardedBy("this")
private final Set<UidRange> mBlockedUidsAsToldToNetd = new ArraySet<>();
- // Handle of the user initiating VPN.
- private final int mUserHandle;
+ // The user id of initiating VPN.
+ private final int mUserId;
interface RetryScheduler {
void checkInterruptAndDelay(boolean sleepLonger) throws InterruptedException;
@@ -384,26 +384,26 @@
}
public Vpn(Looper looper, Context context, INetworkManagementService netService,
- @UserIdInt int userHandle, @NonNull KeyStore keyStore) {
- this(looper, context, new Dependencies(), netService, userHandle, keyStore,
+ @UserIdInt int userId, @NonNull KeyStore keyStore) {
+ this(looper, context, new Dependencies(), netService, userId, keyStore,
new SystemServices(context), new Ikev2SessionCreator());
}
@VisibleForTesting
protected Vpn(Looper looper, Context context, Dependencies deps,
INetworkManagementService netService,
- int userHandle, @NonNull KeyStore keyStore, SystemServices systemServices,
+ int userId, @NonNull KeyStore keyStore, SystemServices systemServices,
Ikev2SessionCreator ikev2SessionCreator) {
mContext = context;
mDeps = deps;
mNetd = netService;
- mUserHandle = userHandle;
+ mUserId = userId;
mLooper = looper;
mSystemServices = systemServices;
mIkev2SessionCreator = ikev2SessionCreator;
mPackage = VpnConfig.LEGACY_VPN;
- mOwnerUID = getAppUid(mPackage, mUserHandle);
+ mOwnerUID = getAppUid(mPackage, mUserId);
mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(mPackage);
try {
@@ -613,7 +613,7 @@
PackageManager pm = mContext.getPackageManager();
ApplicationInfo appInfo = null;
try {
- appInfo = pm.getApplicationInfoAsUser(packageName, 0 /*flags*/, mUserHandle);
+ appInfo = pm.getApplicationInfoAsUser(packageName, 0 /*flags*/, mUserId);
} catch (NameNotFoundException unused) {
Log.w(TAG, "Can't find \"" + packageName + "\" when checking always-on support");
}
@@ -624,7 +624,7 @@
final Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
intent.setPackage(packageName);
List<ResolveInfo> services =
- pm.queryIntentServicesAsUser(intent, PackageManager.GET_META_DATA, mUserHandle);
+ pm.queryIntentServicesAsUser(intent, PackageManager.GET_META_DATA, mUserId);
if (services == null || services.size() == 0) {
return false;
}
@@ -769,12 +769,12 @@
final long token = Binder.clearCallingIdentity();
try {
mSystemServices.settingsSecurePutStringForUser(Settings.Secure.ALWAYS_ON_VPN_APP,
- getAlwaysOnPackage(), mUserHandle);
+ getAlwaysOnPackage(), mUserId);
mSystemServices.settingsSecurePutIntForUser(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
- (mAlwaysOn && mLockdown ? 1 : 0), mUserHandle);
+ (mAlwaysOn && mLockdown ? 1 : 0), mUserId);
mSystemServices.settingsSecurePutStringForUser(
LOCKDOWN_ALLOWLIST_SETTING_NAME,
- String.join(",", mLockdownAllowlist), mUserHandle);
+ String.join(",", mLockdownAllowlist), mUserId);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -786,11 +786,11 @@
final long token = Binder.clearCallingIdentity();
try {
final String alwaysOnPackage = mSystemServices.settingsSecureGetStringForUser(
- Settings.Secure.ALWAYS_ON_VPN_APP, mUserHandle);
+ Settings.Secure.ALWAYS_ON_VPN_APP, mUserId);
final boolean alwaysOnLockdown = mSystemServices.settingsSecureGetIntForUser(
- Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN, 0 /*default*/, mUserHandle) != 0;
+ Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN, 0 /*default*/, mUserId) != 0;
final String allowlistString = mSystemServices.settingsSecureGetStringForUser(
- LOCKDOWN_ALLOWLIST_SETTING_NAME, mUserHandle);
+ LOCKDOWN_ALLOWLIST_SETTING_NAME, mUserId);
final List<String> allowedPackages = TextUtils.isEmpty(allowlistString)
? Collections.emptyList() : Arrays.asList(allowlistString.split(","));
setAlwaysOnPackageInternal(
@@ -850,13 +850,13 @@
DeviceIdleInternal idleController =
LocalServices.getService(DeviceIdleInternal.class);
idleController.addPowerSaveTempWhitelistApp(Process.myUid(), alwaysOnPackage,
- VPN_LAUNCH_IDLE_ALLOWLIST_DURATION_MS, mUserHandle, false, "vpn");
+ VPN_LAUNCH_IDLE_ALLOWLIST_DURATION_MS, mUserId, false, "vpn");
// Start the VPN service declared in the app's manifest.
Intent serviceIntent = new Intent(VpnConfig.SERVICE_INTERFACE);
serviceIntent.setPackage(alwaysOnPackage);
try {
- return mContext.startServiceAsUser(serviceIntent, UserHandle.of(mUserHandle)) != null;
+ return mContext.startServiceAsUser(serviceIntent, UserHandle.of(mUserId)) != null;
} catch (RuntimeException e) {
Log.e(TAG, "VpnService " + serviceIntent + " failed to start", e);
return false;
@@ -958,7 +958,7 @@
// We can't just check that packageName matches mPackage, because if the app was uninstalled
// and reinstalled it will no longer be prepared. Similarly if there is a shared UID, the
// calling package may not be the same as the prepared package. Check both UID and package.
- return getAppUid(packageName, mUserHandle) == mOwnerUID && mPackage.equals(packageName);
+ return getAppUid(packageName, mUserId) == mOwnerUID && mPackage.equals(packageName);
}
/** Prepare the VPN for the given package. Does not perform permission checks. */
@@ -998,7 +998,7 @@
Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
mPackage = newPackage;
- mOwnerUID = getAppUid(newPackage, mUserHandle);
+ mOwnerUID = getAppUid(newPackage, mUserId);
mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(newPackage);
try {
mNetd.allowProtect(mOwnerUID);
@@ -1019,7 +1019,7 @@
// Check if the caller is authorized.
enforceControlPermissionOrInternalCaller();
- final int uid = getAppUid(packageName, mUserHandle);
+ final int uid = getAppUid(packageName, mUserId);
if (uid == -1 || VpnConfig.LEGACY_VPN.equals(packageName)) {
// Authorization for nonexistent packages (or fake ones) can't be updated.
return false;
@@ -1095,14 +1095,14 @@
|| isVpnServicePreConsented(context, packageName);
}
- private int getAppUid(final String app, final int userHandle) {
+ private int getAppUid(final String app, final int userId) {
if (VpnConfig.LEGACY_VPN.equals(app)) {
return Process.myUid();
}
PackageManager pm = mContext.getPackageManager();
return Binder.withCleanCallingIdentity(() -> {
try {
- return pm.getPackageUidAsUser(app, userHandle);
+ return pm.getPackageUidAsUser(app, userId);
} catch (NameNotFoundException e) {
return -1;
}
@@ -1116,7 +1116,7 @@
PackageManager pm = mContext.getPackageManager();
try {
ApplicationInfo appInfo =
- pm.getApplicationInfoAsUser(packageName, 0 /*flags*/, mUserHandle);
+ pm.getApplicationInfoAsUser(packageName, 0 /*flags*/, mUserId);
return appInfo.targetSdkVersion >= VERSION_CODES.Q;
} catch (NameNotFoundException unused) {
Log.w(TAG, "Can't find \"" + packageName + "\"");
@@ -1241,7 +1241,7 @@
mNetworkCapabilities.setOwnerUid(mOwnerUID);
mNetworkCapabilities.setAdministratorUids(new int[] {mOwnerUID});
- mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserHandle,
+ mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserId,
mConfig.allowedApplications, mConfig.disallowedApplications));
long token = Binder.clearCallingIdentity();
try {
@@ -1315,7 +1315,7 @@
enforceNotRestrictedUser();
ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
- null, 0, mUserHandle);
+ null, 0, mUserId);
if (info == null) {
throw new SecurityException("Cannot find " + config.user);
}
@@ -1352,7 +1352,7 @@
Connection connection = new Connection();
if (!mContext.bindServiceAsUser(intent, connection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
- new UserHandle(mUserHandle))) {
+ new UserHandle(mUserId))) {
throw new IllegalStateException("Cannot bind " + config.user);
}
@@ -1427,10 +1427,10 @@
}
// Note: Return type guarantees results are deduped and sorted, which callers require.
- private SortedSet<Integer> getAppsUids(List<String> packageNames, int userHandle) {
+ private SortedSet<Integer> getAppsUids(List<String> packageNames, int userId) {
SortedSet<Integer> uids = new TreeSet<>();
for (String app : packageNames) {
- int uid = getAppUid(app, userHandle);
+ int uid = getAppUid(app, userId);
if (uid != -1) uids.add(uid);
}
return uids;
@@ -1444,22 +1444,22 @@
* the UID ranges will match the app list specified there. Otherwise, all UIDs
* in each user and profile will be included.
*
- * @param userHandle The userId to create UID ranges for along with any of its restricted
+ * @param userId The userId to create UID ranges for along with any of its restricted
* profiles.
* @param allowedApplications (optional) List of applications to allow.
* @param disallowedApplications (optional) List of applications to deny.
*/
@VisibleForTesting
- Set<UidRange> createUserAndRestrictedProfilesRanges(@UserIdInt int userHandle,
+ Set<UidRange> createUserAndRestrictedProfilesRanges(@UserIdInt int userId,
@Nullable List<String> allowedApplications,
@Nullable List<String> disallowedApplications) {
final Set<UidRange> ranges = new ArraySet<>();
// Assign the top-level user to the set of ranges
- addUserToRanges(ranges, userHandle, allowedApplications, disallowedApplications);
+ addUserToRanges(ranges, userId, allowedApplications, disallowedApplications);
// If the user can have restricted profiles, assign all its restricted profiles too
- if (canHaveRestrictedProfile(userHandle)) {
+ if (canHaveRestrictedProfile(userId)) {
final long token = Binder.clearCallingIdentity();
List<UserInfo> users;
try {
@@ -1468,7 +1468,7 @@
Binder.restoreCallingIdentity(token);
}
for (UserInfo user : users) {
- if (user.isRestricted() && (user.restrictedProfileParentId == userHandle)) {
+ if (user.isRestricted() && (user.restrictedProfileParentId == userId)) {
addUserToRanges(ranges, user.id, allowedApplications, disallowedApplications);
}
}
@@ -1485,18 +1485,18 @@
* in the user will be included.
*
* @param ranges {@link Set} of {@link UidRange}s to which to add.
- * @param userHandle The userId to add to {@param ranges}.
+ * @param userId The userId to add to {@param ranges}.
* @param allowedApplications (optional) allowlist of applications to include.
* @param disallowedApplications (optional) denylist of applications to exclude.
*/
@VisibleForTesting
- void addUserToRanges(@NonNull Set<UidRange> ranges, @UserIdInt int userHandle,
+ void addUserToRanges(@NonNull Set<UidRange> ranges, @UserIdInt int userId,
@Nullable List<String> allowedApplications,
@Nullable List<String> disallowedApplications) {
if (allowedApplications != null) {
// Add ranges covering all UIDs for allowedApplications.
int start = -1, stop = -1;
- for (int uid : getAppsUids(allowedApplications, userHandle)) {
+ for (int uid : getAppsUids(allowedApplications, userId)) {
if (start == -1) {
start = uid;
} else if (uid != stop + 1) {
@@ -1508,9 +1508,9 @@
if (start != -1) ranges.add(new UidRange(start, stop));
} else if (disallowedApplications != null) {
// Add all ranges for user skipping UIDs for disallowedApplications.
- final UidRange userRange = UidRange.createForUser(userHandle);
+ final UidRange userRange = UidRange.createForUser(userId);
int start = userRange.start;
- for (int uid : getAppsUids(disallowedApplications, userHandle)) {
+ for (int uid : getAppsUids(disallowedApplications, userId)) {
if (uid == start) {
start++;
} else {
@@ -1521,16 +1521,16 @@
if (start <= userRange.stop) ranges.add(new UidRange(start, userRange.stop));
} else {
// Add all UIDs for the user.
- ranges.add(UidRange.createForUser(userHandle));
+ ranges.add(UidRange.createForUser(userId));
}
}
// Returns the subset of the full list of active UID ranges the VPN applies to (mVpnUsers) that
- // apply to userHandle.
- static private List<UidRange> uidRangesForUser(int userHandle, Set<UidRange> existingRanges) {
+ // apply to userId.
+ private static List<UidRange> uidRangesForUser(int userId, Set<UidRange> existingRanges) {
// UidRange#createForUser returns the entire range of UIDs available to a macro-user.
// This is something like 0-99999 ; {@see UserHandle#PER_USER_RANGE}
- final UidRange userRange = UidRange.createForUser(userHandle);
+ final UidRange userRange = UidRange.createForUser(userId);
final List<UidRange> ranges = new ArrayList<>();
for (UidRange range : existingRanges) {
if (userRange.containsRange(range)) {
@@ -1545,15 +1545,15 @@
*
* <p>Should be called on primary ConnectivityService thread.
*/
- public void onUserAdded(int userHandle) {
+ public void onUserAdded(int userId) {
// If the user is restricted tie them to the parent user's VPN
- UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
- if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
+ UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+ if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
synchronized(Vpn.this) {
final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
if (existingRanges != null) {
try {
- addUserToRanges(existingRanges, userHandle, mConfig.allowedApplications,
+ addUserToRanges(existingRanges, userId, mConfig.allowedApplications,
mConfig.disallowedApplications);
// ConnectivityService will call {@link #updateCapabilities} and apply
// those for VPN network.
@@ -1572,16 +1572,16 @@
*
* <p>Should be called on primary ConnectivityService thread.
*/
- public void onUserRemoved(int userHandle) {
+ public void onUserRemoved(int userId) {
// clean up if restricted
- UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
- if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
+ UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+ if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
synchronized(Vpn.this) {
final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
if (existingRanges != null) {
try {
final List<UidRange> removedRanges =
- uidRangesForUser(userHandle, existingRanges);
+ uidRangesForUser(userId, existingRanges);
existingRanges.removeAll(removedRanges);
// ConnectivityService will call {@link #updateCapabilities} and
// apply those for VPN network.
@@ -1639,7 +1639,7 @@
final Set<UidRange> rangesToTellNetdToAdd;
if (enforce) {
final Set<UidRange> rangesThatShouldBeBlocked =
- createUserAndRestrictedProfilesRanges(mUserHandle,
+ createUserAndRestrictedProfilesRanges(mUserId,
/* allowedApplications */ null,
/* disallowedApplications */ exemptedPackages);
@@ -1909,7 +1909,7 @@
private void updateAlwaysOnNotification(DetailedState networkState) {
final boolean visible = (mAlwaysOn && networkState != DetailedState.CONNECTED);
- final UserHandle user = UserHandle.of(mUserHandle);
+ final UserHandle user = UserHandle.of(mUserId);
final long token = Binder.clearCallingIdentity();
try {
final NotificationManager notificationManager = NotificationManager.from(mContext);
@@ -2019,7 +2019,7 @@
private void enforceNotRestrictedUser() {
Binder.withCleanCallingIdentity(() -> {
final UserManager mgr = UserManager.get(mContext);
- final UserInfo user = mgr.getUserInfo(mUserHandle);
+ final UserInfo user = mgr.getUserInfo(mUserId);
if (user.isRestricted()) {
throw new SecurityException("Restricted users cannot configure VPNs");
@@ -2054,9 +2054,9 @@
public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
LinkProperties egress) {
UserManager mgr = UserManager.get(mContext);
- UserInfo user = mgr.getUserInfo(mUserHandle);
+ UserInfo user = mgr.getUserInfo(mUserId);
if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
- new UserHandle(mUserHandle))) {
+ new UserHandle(mUserId))) {
throw new SecurityException("Restricted users cannot establish VPNs");
}
@@ -2984,14 +2984,14 @@
}
private void verifyCallingUidAndPackage(String packageName) {
- if (getAppUid(packageName, mUserHandle) != Binder.getCallingUid()) {
+ if (getAppUid(packageName, mUserId) != Binder.getCallingUid()) {
throw new SecurityException("Mismatched package and UID");
}
}
@VisibleForTesting
String getProfileNameForPackage(String packageName) {
- return Credentials.PLATFORM_VPN + mUserHandle + "_" + packageName;
+ return Credentials.PLATFORM_VPN + mUserId + "_" + packageName;
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index b33aa0a..91fa15a 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -729,7 +729,7 @@
// Sync adapters were able to access the synced account without the accounts
// permission which circumvents our permission model. Therefore, we require
// sync adapters that don't have access to the account to get user consent.
- // This can be noisy, therefore we will white-list sync adapters installed
+ // This can be noisy, therefore we will allowlist sync adapters installed
// before we started checking for account access because they already know
// the account (they run before) which is the genie is out of the bottle.
whiteListExistingSyncAdaptersIfNeeded();
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 09b7828..4787635 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -50,7 +50,7 @@
public static final int REASON_IS_SYNCABLE = -5;
/** Sync started because it has just been set to sync automatically. */
public static final int REASON_SYNC_AUTO = -6;
- /** Sync started because master sync automatically has been set to true. */
+ /** Sync started because global sync automatically has been set to true. */
public static final int REASON_MASTER_SYNC_AUTO = -7;
public static final int REASON_USER_START = -8;
diff --git a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java
index ad04b7d..c33b5f1 100644
--- a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java
+++ b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java
@@ -48,7 +48,7 @@
/**
* Gets the content capture options for the given user and package, or {@code null} if the
- * package is not whitelisted by the service.
+ * package is not allowlisted by the service.
*
* <p><b>NOTE: </b>this method is called by the {@code ActivityManager} service and hence cannot
* hold the main service lock.
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index f42e18a..eb2c7e6 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -190,7 +190,7 @@
// When the short term model is invalidated, we don't necessarily reset it (i.e. clear the
// user's adjustment) immediately, but wait for a drastic enough change in the ambient light.
- // The anchor determines what were the light levels when the user has set her preference, and
+ // The anchor determines what were the light levels when the user has set their preference, and
// we use a relative threshold to determine when to revert to the OEM curve.
private boolean mShortTermModelValid;
private float mShortTermModelAnchor;
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index cc6687f..b1c91a6 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -700,7 +700,7 @@
// Application can specify preferred refresh rate with below attrs.
// @see android.view.WindowManager.LayoutParams#preferredRefreshRate
// @see android.view.WindowManager.LayoutParams#preferredDisplayModeId
- // System also forces some apps like blacklisted app to run at a lower refresh rate.
+ // System also forces some apps like denylisted app to run at a lower refresh rate.
// @see android.R.array#config_highRefreshRateBlacklist
public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 3;
public static final int PRIORITY_APP_REQUEST_SIZE = 4;
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 6672daa..7fc3d2b 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -71,7 +71,7 @@
* <p>See {@code com.android.server.autofill.AutofillManagerService} for a concrete
* (no pun intended) example of how to use it.
*
- * @param <M> "master" service class.
+ * @param <M> "main" service class.
* @param <S> "real" service class.
*
* @hide
diff --git a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
index 16cf7ee..757a5cc 100644
--- a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
@@ -41,7 +41,7 @@
* Companion for {@link AbstractMasterSystemService}, it's the base class for the "real" service
* implementation.
*
- * @param <M> "master" service class.
+ * @param <M> "main" service class.
* @param <S> "real" service class.
*
* @hide
@@ -208,7 +208,7 @@
}
/**
- * Gets the master service.
+ * Gets the main service.
*/
public final M getMaster() {
return mMaster;
@@ -301,7 +301,7 @@
}
/**
- * Removes the service from the master's cache.
+ * Removes the service from the main service's cache.
*/
protected final void removeSelfFromCacheLocked() {
mMaster.removeCachedServiceLocked(mUserId);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 3235b20..bba248c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3201,7 +3201,7 @@
boolean res = false;
if (mCurMethod != null) {
if (DEBUG) Slog.d(TAG, "showCurrentInputLocked: mCurToken=" + mCurToken);
- // create a dummy token for IMS so that IMS cannot inject windows into client app.
+ // create a placeholder token for IMS so that IMS cannot inject windows into client app.
Binder showInputToken = new Binder();
mShowRequestWindowMap.put(showInputToken, windowToken);
executeOrSendMessage(mCurMethod, mCaller.obtainMessageIIOOO(
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
index 06e7a76..56982a8 100644
--- a/services/core/java/com/android/server/location/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -16,8 +16,6 @@
package com.android.server.location;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
-
import android.annotation.Nullable;
import android.location.Location;
import android.location.util.identity.CallerIdentity;
@@ -342,8 +340,7 @@
*/
public final void setRequest(ProviderRequest request) {
// all calls into the provider must be moved onto the provider thread to prevent deadlock
- mExecutor.execute(obtainRunnable(AbstractLocationProvider::onSetRequest, this, request)
- .recycleOnUse());
+ mExecutor.execute(() -> onSetRequest(request));
}
/**
@@ -356,13 +353,7 @@
*/
public final void sendExtraCommand(int uid, int pid, String command, Bundle extras) {
// all calls into the provider must be moved onto the provider thread to prevent deadlock
-
- // the integer boxing done here likely cancels out any gains from removing lambda
- // allocation, but since this an infrequently used api with no real performance needs, we
- // we use pooled lambdas anyways for consistency.
- mExecutor.execute(
- obtainRunnable(AbstractLocationProvider::onExtraCommand, this, uid, pid, command,
- extras).recycleOnUse());
+ mExecutor.execute(() -> onExtraCommand(uid, pid, command, extras));
}
/**
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index f4d0a62..807784d 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -553,8 +553,9 @@
}
@Override
- public void registerLocationListener(LocationRequest request, ILocationListener listener,
- String packageName, String attributionTag, String listenerId) {
+ public void registerLocationListener(String provider, LocationRequest request,
+ ILocationListener listener, String packageName, String attributionTag,
+ String listenerId) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
listenerId);
int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
@@ -570,16 +571,16 @@
request = validateAndSanitizeLocationRequest(request, permissionLevel);
- LocationProviderManager manager = getLocationProviderManager(request.getProvider());
+ LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
- "provider \"" + request.getProvider() + "\" does not exist");
+ "provider \"" + provider + "\" does not exist");
manager.registerLocationRequest(request, identity, permissionLevel, listener);
}
@Override
- public void registerLocationPendingIntent(LocationRequest request, PendingIntent pendingIntent,
- String packageName, String attributionTag) {
+ public void registerLocationPendingIntent(String provider, LocationRequest request,
+ PendingIntent pendingIntent, String packageName, String attributionTag) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
AppOpsManager.toReceiverId(pendingIntent));
int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
@@ -592,24 +593,22 @@
request = validateAndSanitizeLocationRequest(request, permissionLevel);
- LocationProviderManager manager = getLocationProviderManager(request.getProvider());
+ LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
- "provider \"" + request.getProvider() + "\" does not exist");
+ "provider \"" + provider + "\" does not exist");
manager.registerLocationRequest(request, identity, permissionLevel, pendingIntent);
}
private LocationRequest validateAndSanitizeLocationRequest(LocationRequest request,
@PermissionLevel int permissionLevel) {
- Objects.requireNonNull(request.getProvider());
-
WorkSource workSource = request.getWorkSource();
if (workSource != null && !workSource.isEmpty()) {
mContext.enforceCallingOrSelfPermission(
permission.UPDATE_DEVICE_STATS,
"setting a work source requires " + permission.UPDATE_DEVICE_STATS);
}
- if (request.getHideFromAppOps()) {
+ if (request.isHiddenFromAppOps()) {
mContext.enforceCallingOrSelfPermission(
permission.UPDATE_APP_OPS_STATS,
"hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS);
@@ -620,12 +619,12 @@
"ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
}
- LocationRequest sanitized = new LocationRequest(request);
+ LocationRequest.Builder sanitized = new LocationRequest.Builder(request);
if (mContext.checkCallingPermission(permission.LOCATION_HARDWARE) != PERMISSION_GRANTED) {
- sanitized.setLowPowerMode(false);
+ sanitized.setLowPower(false);
}
if (permissionLevel < PERMISSION_FINE) {
- switch (sanitized.getQuality()) {
+ switch (request.getQuality()) {
case LocationRequest.ACCURACY_FINE:
sanitized.setQuality(LocationRequest.ACCURACY_BLOCK);
break;
@@ -634,24 +633,21 @@
break;
}
- if (sanitized.getInterval() < FASTEST_COARSE_INTERVAL_MS) {
- sanitized.setInterval(FASTEST_COARSE_INTERVAL_MS);
+ if (request.getIntervalMillis() < FASTEST_COARSE_INTERVAL_MS) {
+ sanitized.setIntervalMillis(FASTEST_COARSE_INTERVAL_MS);
}
- if (sanitized.getFastestInterval() < FASTEST_COARSE_INTERVAL_MS) {
- sanitized.setFastestInterval(FASTEST_COARSE_INTERVAL_MS);
+ if (request.getMinUpdateIntervalMillis() < FASTEST_COARSE_INTERVAL_MS) {
+ sanitized.clearMinUpdateIntervalMillis();
}
}
- if (sanitized.getFastestInterval() > sanitized.getInterval()) {
- sanitized.setFastestInterval(request.getInterval());
- }
- if (sanitized.getWorkSource() != null) {
- if (sanitized.getWorkSource().isEmpty()) {
+ if (request.getWorkSource() != null) {
+ if (request.getWorkSource().isEmpty()) {
sanitized.setWorkSource(null);
- } else if (sanitized.getWorkSource().getPackageName(0) == null) {
+ } else if (request.getWorkSource().getPackageName(0) == null) {
Log.w(TAG, "received (and ignoring) illegal worksource with no package name");
sanitized.setWorkSource(null);
} else {
- List<WorkChain> workChains = sanitized.getWorkSource().getWorkChains();
+ List<WorkChain> workChains = request.getWorkSource().getWorkChains();
if (workChains != null && !workChains.isEmpty() && workChains.get(
0).getAttributionTag() == null) {
Log.w(TAG,
@@ -661,7 +657,7 @@
}
}
- return sanitized;
+ return sanitized.build();
}
@Override
@@ -679,8 +675,7 @@
}
@Override
- public Location getLastLocation(LocationRequest request, String packageName,
- String attributionTag) {
+ public Location getLastLocation(String provider, String packageName, String attributionTag) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
identity.getPid());
@@ -690,15 +685,12 @@
// clients in the system process must have an attribution tag set
Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null);
- request = validateAndSanitizeLocationRequest(request, permissionLevel);
-
- LocationProviderManager manager = getLocationProviderManager(request.getProvider());
+ LocationProviderManager manager = getLocationProviderManager(provider);
if (manager == null) {
return null;
}
- Location location = manager.getLastLocation(identity, permissionLevel,
- request.isLocationSettingsIgnored());
+ Location location = manager.getLastLocation(identity, permissionLevel, false);
// lastly - note app ops
if (!mInjector.getAppOpsHelper().noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
@@ -710,7 +702,7 @@
}
@Override
- public void getCurrentLocation(LocationRequest request,
+ public void getCurrentLocation(String provider, LocationRequest request,
ICancellationSignal cancellationTransport, ILocationCallback consumer,
String packageName, String attributionTag, String listenerId) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
@@ -725,9 +717,9 @@
request = validateAndSanitizeLocationRequest(request, permissionLevel);
- LocationProviderManager manager = getLocationProviderManager(request.getProvider());
+ LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
- "provider \"" + request.getProvider() + "\" does not exist");
+ "provider \"" + provider + "\" does not exist");
manager.getCurrentLocation(request, identity, permissionLevel, cancellationTransport,
consumer);
diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java
index 1815a85..2a5e035 100644
--- a/services/core/java/com/android/server/location/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/LocationProviderManager.java
@@ -29,12 +29,14 @@
import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+import static com.android.internal.location.ProviderRequest.EMPTY_REQUEST;
import static com.android.server.location.LocationManagerService.D;
import static com.android.server.location.LocationManagerService.TAG;
import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import static com.android.server.location.LocationPermissions.PERMISSION_NONE;
+import static java.lang.Math.min;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import android.annotation.Nullable;
@@ -76,6 +78,7 @@
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;
@@ -110,7 +113,8 @@
import java.util.Objects;
class LocationProviderManager extends
- ListenerMultiplexer<Object, LocationRequest, LocationProviderManager.LocationTransport,
+ ListenerMultiplexer<Object, LocationProviderManager.LocationTransport,
+ LocationProviderManager.LocationListenerOperation,
LocationProviderManager.Registration, ProviderRequest> implements
AbstractLocationProvider.Listener {
@@ -120,16 +124,16 @@
private static final String WAKELOCK_TAG = "*location*";
private static final long WAKELOCK_TIMEOUT_MS = 30 * 1000;
- // maximum interval to be considered "high power" request
+ // max interval to be considered "high power" request
private static final long MAX_HIGH_POWER_INTERVAL_MS = 5 * 60 * 1000;
- // maximum age of a location before it is no longer considered "current"
+ // max age of a location before it is no longer considered "current"
private static final long MAX_CURRENT_LOCATION_AGE_MS = 10 * 1000;
// max timeout allowed for getting the current location
private static final long GET_CURRENT_LOCATION_MAX_TIMEOUT_MS = 30 * 1000;
- // maximum jitter allowed for fastest interval evaluation
+ // max jitter allowed for fastest interval evaluation
private static final int MAX_FASTEST_INTERVAL_JITTER_MS = 100;
protected interface LocationTransport {
@@ -213,8 +217,15 @@
}
}
- protected abstract class Registration extends
- RemoteListenerRegistration<LocationRequest, LocationTransport> {
+ protected interface LocationListenerOperation extends ListenerOperation<LocationTransport> {
+ /**
+ * Must be implemented to return the location this operation intends to deliver.
+ */
+ Location getLocation();
+ }
+
+ protected abstract class Registration extends RemoteListenerRegistration<LocationRequest,
+ LocationTransport, LocationListenerOperation> {
@PermissionLevel protected final int mPermissionLevel;
private final WorkSource mWorkSource;
@@ -225,9 +236,11 @@
private LocationRequest mProviderLocationRequest;
private boolean mIsUsingHighPower;
+ @Nullable private Location mLastLocation = null;
+
protected Registration(LocationRequest request, CallerIdentity identity,
LocationTransport transport, @PermissionLevel int permissionLevel) {
- super(TAG, Objects.requireNonNull(request), identity, transport);
+ super(Objects.requireNonNull(request), identity, transport);
Preconditions.checkArgument(permissionLevel > PERMISSION_NONE);
mPermissionLevel = permissionLevel;
@@ -290,8 +303,12 @@
protected void onProviderListenerUnregister() {}
@Override
- protected final ListenerOperation<LocationTransport> onActive() {
- if (!getRequest().getHideFromAppOps()) {
+ protected final LocationListenerOperation onActive() {
+ if (Build.IS_DEBUGGABLE) {
+ Preconditions.checkState(Thread.holdsLock(mLock));
+ }
+
+ if (!getRequest().isHiddenFromAppOps()) {
mLocationAttributionHelper.reportLocationStart(getIdentity(), getName(), getKey());
}
onHighPowerUsageChanged();
@@ -299,27 +316,27 @@
}
@Override
- protected final ListenerOperation<LocationTransport> onInactive() {
+ protected final LocationListenerOperation onInactive() {
+ if (Build.IS_DEBUGGABLE) {
+ Preconditions.checkState(Thread.holdsLock(mLock));
+ }
+
onHighPowerUsageChanged();
- if (!getRequest().getHideFromAppOps()) {
+ if (!getRequest().isHiddenFromAppOps()) {
mLocationAttributionHelper.reportLocationStop(getIdentity(), getName(), getKey());
}
return null;
}
@Override
- public final <Listener> void onOperationFailure(ListenerOperation<Listener> operation,
- Exception e) {
- synchronized (mLock) {
- super.onOperationFailure(operation, e);
- }
- }
-
- @Override
public final LocationRequest getRequest() {
return mProviderLocationRequest;
}
+ public final Location getLastDeliveredLocation() {
+ return mLastLocation;
+ }
+
public final boolean isForeground() {
return mForeground;
}
@@ -343,7 +360,7 @@
if (isUsingHighPower != mIsUsingHighPower) {
mIsUsingHighPower = isUsingHighPower;
- if (!getRequest().getHideFromAppOps()) {
+ if (!getRequest().isHiddenFromAppOps()) {
if (mIsUsingHighPower) {
mLocationAttributionHelper.reportHighPowerLocationStart(
getIdentity(), getName(), getKey());
@@ -362,8 +379,8 @@
}
return isActive()
- && getRequest().getInterval() < MAX_HIGH_POWER_INTERVAL_MS
- && getProperties().mPowerRequirement == Criteria.POWER_HIGH;
+ && getRequest().getIntervalMillis() < MAX_HIGH_POWER_INTERVAL_MS
+ && getProperties().getPowerRequirement() == Criteria.POWER_HIGH;
}
@GuardedBy("mLock")
@@ -448,26 +465,26 @@
}
private LocationRequest calculateProviderLocationRequest() {
- LocationRequest newRequest = new LocationRequest(super.getRequest());
+ LocationRequest.Builder builder = new LocationRequest.Builder(super.getRequest());
- if (newRequest.isLocationSettingsIgnored()) {
+ if (super.getRequest().isLocationSettingsIgnored()) {
// if we are not currently allowed use location settings ignored, disable it
if (!mSettingsHelper.getIgnoreSettingsPackageWhitelist().contains(
getIdentity().getPackageName()) && !mLocationManagerInternal.isProvider(
null, getIdentity())) {
- newRequest.setLocationSettingsIgnored(false);
+ builder.setLocationSettingsIgnored(false);
}
}
- if (!newRequest.isLocationSettingsIgnored() && !isThrottlingExempt()) {
+ if (!super.getRequest().isLocationSettingsIgnored() && !isThrottlingExempt()) {
// throttle in the background
if (!mForeground) {
- newRequest.setInterval(Math.max(newRequest.getInterval(),
+ builder.setIntervalMillis(Math.max(super.getRequest().getIntervalMillis(),
mSettingsHelper.getBackgroundThrottleIntervalMs()));
}
}
- return newRequest;
+ return builder.build();
}
private boolean isThrottlingExempt() {
@@ -479,8 +496,17 @@
return mLocationManagerInternal.isProvider(null, getIdentity());
}
+ @GuardedBy("mLock")
+ @Override
+ protected final LocationListenerOperation onExecuteOperation(
+ LocationListenerOperation operation) {
+ mLastLocation = operation.getLocation();
+ return super.onExecuteOperation(operation);
+ }
+
+ @GuardedBy("mLock")
@Nullable
- abstract ListenerOperation<LocationTransport> acceptLocationChange(Location fineLocation);
+ abstract LocationListenerOperation acceptLocationChange(Location fineLocation);
@Override
public String toString() {
@@ -513,7 +539,6 @@
private final PowerManager.WakeLock mWakeLock;
private volatile ProviderTransport mProviderTransport;
- @Nullable private Location mLastLocation = null;
private int mNumLocationsDelivered = 0;
private long mExpirationRealtimeMs = Long.MAX_VALUE;
@@ -605,7 +630,7 @@
@GuardedBy("mLock")
@Nullable
@Override
- ListenerOperation<LocationTransport> acceptLocationChange(Location fineLocation) {
+ LocationListenerOperation acceptLocationChange(Location fineLocation) {
if (Build.IS_DEBUGGABLE) {
Preconditions.checkState(Thread.holdsLock(mLock));
}
@@ -630,19 +655,21 @@
throw new AssertionError();
}
- if (mLastLocation != null) {
+ Location lastDeliveredLocation = getLastDeliveredLocation();
+ if (lastDeliveredLocation != null) {
// check fastest interval
long deltaMs = NANOSECONDS.toMillis(
location.getElapsedRealtimeNanos()
- - mLastLocation.getElapsedRealtimeNanos());
- if (deltaMs < getRequest().getFastestInterval() - MAX_FASTEST_INTERVAL_JITTER_MS) {
+ - lastDeliveredLocation.getElapsedRealtimeNanos());
+ if (deltaMs < getRequest().getMinUpdateIntervalMillis()
+ - MAX_FASTEST_INTERVAL_JITTER_MS) {
return null;
}
// check smallest displacement
- double smallestDisplacement = getRequest().getSmallestDisplacement();
- if (smallestDisplacement > 0.0 && location.distanceTo(mLastLocation)
- <= smallestDisplacement) {
+ double smallestDisplacementM = getRequest().getMinUpdateDistanceMeters();
+ if (smallestDisplacementM > 0.0 && location.distanceTo(lastDeliveredLocation)
+ <= smallestDisplacementM) {
return null;
}
}
@@ -656,10 +683,12 @@
return null;
}
- // update last location
- mLastLocation = location;
+ return new LocationListenerOperation() {
+ @Override
+ public Location getLocation() {
+ return location;
+ }
- return new ListenerOperation<LocationTransport>() {
@Override
public void onPreExecute() {
// don't acquire a wakelock for mock locations to prevent abuse
@@ -692,7 +721,7 @@
if (success) {
// check num updates - if successful then this function will always be run
// from the same thread, and no additional synchronization is necessary
- boolean remove = ++mNumLocationsDelivered >= getRequest().getNumUpdates();
+ boolean remove = ++mNumLocationsDelivered >= getRequest().getMaxUpdates();
if (remove) {
if (D) {
Log.d(TAG, "removing " + getIdentity() + " from " + mName
@@ -718,8 +747,12 @@
// we choose not to hold a wakelock for provider enabled changed events
executeSafely(getExecutor(), () -> mProviderTransport,
- listener -> listener.deliverOnProviderEnabledChanged(mName, enabled));
+ listener -> listener.deliverOnProviderEnabledChanged(mName, enabled),
+ this::onProviderOperationFailure);
}
+
+ protected abstract void onProviderOperationFailure(
+ ListenerOperation<ProviderTransport> operation, Exception exception);
}
protected final class LocationListenerRegistration extends LocationRegistration implements
@@ -747,6 +780,28 @@
}
@Override
+ protected void onProviderOperationFailure(ListenerOperation<ProviderTransport> operation,
+ Exception exception) {
+ onTransportFailure(exception);
+ }
+
+ @Override
+ public void onOperationFailure(LocationListenerOperation operation, Exception exception) {
+ onTransportFailure(exception);
+ }
+
+ private void onTransportFailure(Exception exception) {
+ if (exception instanceof RemoteException) {
+ Log.w(TAG, "registration " + this + " removed", exception);
+ synchronized (mLock) {
+ remove();
+ }
+ } else {
+ throw new AssertionError(exception);
+ }
+ }
+
+ @Override
public void binderDied() {
try {
if (D) {
@@ -786,6 +841,28 @@
}
@Override
+ protected void onProviderOperationFailure(ListenerOperation<ProviderTransport> operation,
+ Exception exception) {
+ onTransportFailure(exception);
+ }
+
+ @Override
+ public void onOperationFailure(LocationListenerOperation operation, Exception exception) {
+ onTransportFailure(exception);
+ }
+
+ private void onTransportFailure(Exception exception) {
+ if (exception instanceof PendingIntent.CanceledException) {
+ Log.w(TAG, "registration " + this + " removed", exception);
+ synchronized (mLock) {
+ remove();
+ }
+ } else {
+ throw new AssertionError(exception);
+ }
+ }
+
+ @Override
public void onCancelled(PendingIntent intent) {
synchronized (mLock) {
remove();
@@ -836,13 +913,17 @@
0, this, FgThread.getHandler(), getWorkSource());
}
- // start listening for provider enabled/disabled events
- addEnabledListener(this);
+ // if this request is ignoring location settings, then we don't want to immediately fail
+ // it if the provider is disabled or becomes disabled.
+ if (!getRequest().isLocationSettingsIgnored()) {
+ // start listening for provider enabled/disabled events
+ addEnabledListener(this);
- // if the provider is currently disabled fail immediately
- int userId = getIdentity().getUserId();
- if (!getRequest().isLocationSettingsIgnored() && !isEnabled(userId)) {
- deliverLocation(null);
+ // if the provider is currently disabled fail immediately
+ int userId = getIdentity().getUserId();
+ if (!getRequest().isLocationSettingsIgnored() && !isEnabled(userId)) {
+ deliverLocation(null);
+ }
}
}
@@ -878,7 +959,7 @@
@GuardedBy("mLock")
@Nullable
@Override
- ListenerOperation<LocationTransport> acceptLocationChange(@Nullable Location fineLocation) {
+ LocationListenerOperation acceptLocationChange(@Nullable Location fineLocation) {
if (Build.IS_DEBUGGABLE) {
Preconditions.checkState(Thread.holdsLock(mLock));
}
@@ -907,19 +988,35 @@
}
}
- 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);
+ return new LocationListenerOperation() {
+ @Override
+ public Location getLocation() {
+ return location;
}
- // we currently don't hold a wakelock for getCurrentLocation deliveries
- listener.deliverOnLocationChanged(deliveryLocation, null);
+ @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);
+ }
- synchronized (mLock) {
- remove();
+ // we currently don't hold a wakelock for getCurrentLocation deliveries
+ try {
+ listener.deliverOnLocationChanged(deliveryLocation, null);
+ } catch (Exception exception) {
+ if (exception instanceof RemoteException) {
+ Log.w(TAG, "registration " + this + " failed", exception);
+ } else {
+ throw new AssertionError(exception);
+ }
+ }
+
+ synchronized (mLock) {
+ remove();
+ }
}
};
}
@@ -1052,6 +1149,11 @@
mProvider = new MockableLocationProvider(mLock, this);
}
+ @Override
+ public String getTag() {
+ return TAG;
+ }
+
public void startManager() {
synchronized (mLock) {
mStarted = true;
@@ -1223,7 +1325,7 @@
throw new IllegalArgumentException(mName + " provider is not a test provider");
}
- return mProvider.getCurrentRequest().locationRequests;
+ return mProvider.getCurrentRequest().getLocationRequests();
}
}
@@ -1258,8 +1360,7 @@
/**
* This function does not perform any permissions or safety checks, by calling it you are
- * committing to performing all applicable checks yourself. Prefer
- * {@link #getLastLocation(CallerIdentity, int, boolean)} where possible.
+ * committing to performing all applicable checks yourself.
*/
@Nullable
public Location getLastLocationUnsafe(int userId, @PermissionLevel int permissionLevel,
@@ -1326,10 +1427,10 @@
public void getCurrentLocation(LocationRequest request, CallerIdentity callerIdentity,
int permissionLevel, ICancellationSignal cancellationTransport,
ILocationCallback callback) {
- Preconditions.checkArgument(mName.equals(request.getProvider()));
-
- if (request.getExpireIn() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) {
- request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
+ if (request.getDurationMillis() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) {
+ request = new LocationRequest.Builder(request)
+ .setDurationMillis(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS)
+ .build();
}
GetCurrentLocationListenerRegistration registration =
@@ -1404,8 +1505,6 @@
public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity,
@PermissionLevel int permissionLevel, ILocationListener listener) {
- Preconditions.checkArgument(mName.equals(request.getProvider()));
-
synchronized (mLock) {
long identity = Binder.clearCallingIdentity();
try {
@@ -1424,8 +1523,6 @@
public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity,
@PermissionLevel int permissionLevel, PendingIntent pendingIntent) {
- Preconditions.checkArgument(mName.equals(request.getProvider()));
-
synchronized (mLock) {
long identity = Binder.clearCallingIdentity();
try {
@@ -1517,17 +1614,17 @@
LocationStatsEnums.USAGE_STARTED,
LocationStatsEnums.API_REQUEST_LOCATION_UPDATES,
registration.getIdentity().getPackageName(),
+ mName,
registration.getRequest(),
key instanceof PendingIntent,
- key instanceof IBinder,
- /* geofence= */ null,
- registration.isForeground());
+ /* geofence= */ key instanceof IBinder,
+ null, registration.isForeground());
mLocationRequestStatistics.startRequesting(
registration.getIdentity().getPackageName(),
registration.getIdentity().getAttributionTag(),
mName,
- registration.getRequest().getInterval(),
+ registration.getRequest().getIntervalMillis(),
registration.isForeground());
}
@@ -1547,16 +1644,17 @@
LocationStatsEnums.USAGE_ENDED,
LocationStatsEnums.API_REQUEST_LOCATION_UPDATES,
registration.getIdentity().getPackageName(),
+ mName,
registration.getRequest(),
key instanceof PendingIntent,
- key instanceof IBinder,
- /* geofence= */ null,
- registration.isForeground());
+ /* geofence= */ key instanceof IBinder,
+ null, registration.isForeground());
}
@GuardedBy("mLock")
@Override
- protected boolean registerWithService(ProviderRequest mergedRequest) {
+ protected boolean registerWithService(ProviderRequest mergedRequest,
+ Collection<Registration> registrations) {
if (Build.IS_DEBUGGABLE) {
Preconditions.checkState(Thread.holdsLock(mLock));
}
@@ -1568,8 +1666,8 @@
@GuardedBy("mLock")
@Override
protected boolean reregisterWithService(ProviderRequest oldRequest,
- ProviderRequest newRequest) {
- return registerWithService(newRequest);
+ ProviderRequest newRequest, Collection<Registration> registrations) {
+ return registerWithService(newRequest, registrations);
}
@GuardedBy("mLock")
@@ -1578,7 +1676,8 @@
if (Build.IS_DEBUGGABLE) {
Preconditions.checkState(Thread.holdsLock(mLock));
}
- mProvider.setRequest(ProviderRequest.EMPTY_REQUEST);
+
+ mProvider.setRequest(EMPTY_REQUEST);
}
@GuardedBy("mLock")
@@ -1630,56 +1729,57 @@
@GuardedBy("mLock")
@Override
- protected ProviderRequest mergeRequests(Collection<Registration> registrations) {
+ protected ProviderRequest mergeRegistrations(Collection<Registration> registrations) {
if (Build.IS_DEBUGGABLE) {
Preconditions.checkState(Thread.holdsLock(mLock));
}
- ProviderRequest.Builder providerRequest = new ProviderRequest.Builder();
- // initialize the low power mode to true and set to false if any of the records requires
- providerRequest.setLowPowerMode(true);
-
ArrayList<Registration> providerRegistrations = new ArrayList<>(registrations.size());
+
+ long intervalMs = Long.MAX_VALUE;
+ boolean locationSettingsIgnored = false;
+ boolean lowPower = true;
+ ArrayList<LocationRequest> locationRequests = new ArrayList<>(registrations.size());
for (Registration registration : registrations) {
LocationRequest locationRequest = registration.getRequest();
- if (locationRequest.isLocationSettingsIgnored()) {
- providerRequest.setLocationSettingsIgnored(true);
- }
- if (!locationRequest.isLowPowerMode()) {
- providerRequest.setLowPowerMode(false);
+ // passive requests do not contribute to the provider
+ if (locationRequest.getIntervalMillis() == LocationRequest.PASSIVE_INTERVAL) {
+ continue;
}
- providerRequest.setInterval(
- Math.min(locationRequest.getInterval(), providerRequest.getInterval()));
providerRegistrations.add(registration);
+ intervalMs = min(locationRequest.getIntervalMillis(), intervalMs);
+ locationSettingsIgnored |= locationRequest.isLocationSettingsIgnored();
+ lowPower &= locationRequest.isLowPower();
+ locationRequests.add(locationRequest);
}
- // collect contributing location requests
- ArrayList<LocationRequest> providerRequests = new ArrayList<>(providerRegistrations.size());
- final int registrationsSize = providerRegistrations.size();
- for (int i = 0; i < registrationsSize; i++) {
- providerRequests.add(providerRegistrations.get(i).getRequest());
- }
-
- providerRequest.setLocationRequests(providerRequests);
-
// calculate who to blame for power in a somewhat arbitrary fashion. we pick a threshold
// interval slightly higher that the minimum interval, and spread the blame across all
- // contributing registrations under that threshold.
- long thresholdIntervalMs = (providerRequest.getInterval() + 1000) * 3 / 2;
+ // contributing registrations under that threshold (since worksource does not allow us to
+ // represent differing power blame ratios).
+ WorkSource workSource = new WorkSource();
+ long thresholdIntervalMs = (intervalMs + 1000) * 3 / 2;
if (thresholdIntervalMs < 0) {
- // handle overflow
- thresholdIntervalMs = Long.MAX_VALUE;
+ // handle overflow by setting to one below the passive interval
+ thresholdIntervalMs = Long.MAX_VALUE - 1;
}
- for (int i = 0; i < registrationsSize; i++) {
- LocationRequest request = providerRegistrations.get(i).getRequest();
- if (request.getInterval() <= thresholdIntervalMs) {
- providerRequest.getWorkSource().add(providerRegistrations.get(i).getWorkSource());
+ final int providerRegistrationsSize = providerRegistrations.size();
+ for (int i = 0; i < providerRegistrationsSize; i++) {
+ Registration registration = providerRegistrations.get(i);
+ if (registration.getRequest().getIntervalMillis() <= thresholdIntervalMs) {
+ workSource.add(providerRegistrations.get(i).getWorkSource());
}
}
- return providerRequest.build();
+ return new ProviderRequest.Builder()
+ .setIntervalMillis(intervalMs)
+ .setLocationSettingsIgnored(locationSettingsIgnored)
+ .setLowPower(lowPower)
+ .setLocationRequests(locationRequests)
+ .setWorkSource(workSource)
+ .build();
}
private void onUserChanged(int userId, int change) {
@@ -1815,9 +1915,7 @@
}
// attempt listener delivery
- deliverToListeners(registration -> {
- return registration.acceptLocationChange(location);
- });
+ deliverToListeners(registration -> registration.acceptLocationChange(location));
}
@GuardedBy("mLock")
@@ -1952,7 +2050,8 @@
public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
synchronized (mLock) {
- ipw.print(mName + " provider");
+ ipw.print(mName);
+ ipw.print(" provider");
if (mProvider.isMock()) {
ipw.print(" [mock]");
}
@@ -1964,12 +2063,15 @@
int[] userIds = mUserInfoHelper.getRunningUserIds();
for (int userId : userIds) {
if (userIds.length != 1) {
- ipw.println("user " + userId + ":");
+ ipw.print("user ");
+ ipw.print(userId);
+ ipw.println(":");
ipw.increaseIndent();
}
- ipw.println(
- "last location=" + getLastLocationUnsafe(userId, PERMISSION_FINE, false));
- ipw.println("enabled=" + isEnabled(userId));
+ ipw.print("last location=");
+ ipw.println(getLastLocationUnsafe(userId, PERMISSION_FINE, false));
+ ipw.print("enabled=");
+ ipw.println(isEnabled(userId));
if (userIds.length != 1) {
ipw.decreaseIndent();
}
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index d778d24..b111c15 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -101,7 +101,7 @@
ProviderRequest request = mRequest;
if (!request.equals(ProviderRequest.EMPTY_REQUEST)) {
- provider.setRequest(request, request.workSource);
+ provider.setRequest(request, request.getWorkSource());
}
}
}
@@ -119,7 +119,7 @@
mRequest = request;
mServiceWatcher.runOnBinder(binder -> {
ILocationProvider provider = ILocationProvider.Stub.asInterface(binder);
- provider.setRequest(request, request.workSource);
+ provider.setRequest(request, request.getWorkSource());
});
}
diff --git a/services/core/java/com/android/server/location/PassiveLocationProviderManager.java b/services/core/java/com/android/server/location/PassiveLocationProviderManager.java
index d4999ab..afeb644 100644
--- a/services/core/java/com/android/server/location/PassiveLocationProviderManager.java
+++ b/services/core/java/com/android/server/location/PassiveLocationProviderManager.java
@@ -20,11 +20,16 @@
import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
+import android.location.LocationRequest;
import android.os.Binder;
+import com.android.internal.location.ProviderRequest;
import com.android.internal.util.Preconditions;
import com.android.server.location.util.Injector;
+import java.util.ArrayList;
+import java.util.Collection;
+
class PassiveLocationProviderManager extends LocationProviderManager {
PassiveLocationProviderManager(Context context, Injector injector) {
@@ -57,4 +62,20 @@
}
}
}
+
+ @Override
+ protected ProviderRequest mergeRegistrations(Collection<Registration> registrations) {
+ ProviderRequest.Builder providerRequest = new ProviderRequest.Builder()
+ .setIntervalMillis(0);
+
+ ArrayList<LocationRequest> requests = new ArrayList<>(registrations.size());
+ for (Registration registration : registrations) {
+ requests.add(registration.getRequest());
+ if (registration.getRequest().isLocationSettingsIgnored()) {
+ providerRequest.setLocationSettingsIgnored(true);
+ }
+ }
+
+ return providerRequest.setLocationRequests(requests).build();
+ }
}
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 2d7f028..58e6d59 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -16,6 +16,7 @@
package com.android.server.location.geofence;
+import static android.location.LocationManager.FUSED_PROVIDER;
import static android.location.LocationManager.KEY_PROXIMITY_ENTERING;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
@@ -40,6 +41,7 @@
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;
@@ -58,7 +60,7 @@
* Manages all geofences.
*/
public class GeofenceManager extends
- ListenerMultiplexer<GeofenceKey, Geofence, PendingIntent,
+ ListenerMultiplexer<GeofenceKey, PendingIntent, ListenerOperation<PendingIntent>,
GeofenceManager.GeofenceRegistration, LocationRequest> implements
LocationListener {
@@ -93,7 +95,7 @@
protected GeofenceRegistration(Geofence geofence, CallerIdentity identity,
PendingIntent pendingIntent) {
- super(TAG, geofence, identity, pendingIntent);
+ super(geofence, identity, pendingIntent);
mCenter = new Location("");
mCenter.setLatitude(geofence.getLatitude());
@@ -272,6 +274,11 @@
mLocationUsageLogger = injector.getLocationUsageLogger();
}
+ @Override
+ public String getTag() {
+ return TAG;
+ }
+
private LocationManager getLocationManager() {
synchronized (mLock) {
if (mLocationManager == null) {
@@ -350,11 +357,11 @@
LocationStatsEnums.USAGE_ENDED,
LocationStatsEnums.API_REQUEST_GEOFENCE,
registration.getIdentity().getPackageName(),
+ null,
/* LocationRequest= */ null,
/* hasListener= */ false,
true,
- registration.getRequest(),
- true);
+ registration.getRequest(), true);
}
@Override
@@ -363,16 +370,18 @@
LocationStatsEnums.USAGE_ENDED,
LocationStatsEnums.API_REQUEST_GEOFENCE,
registration.getIdentity().getPackageName(),
+ null,
/* LocationRequest= */ null,
/* hasListener= */ false,
true,
- registration.getRequest(),
- true);
+ registration.getRequest(), true);
}
@Override
- protected boolean registerWithService(LocationRequest locationRequest) {
- getLocationManager().requestLocationUpdates(locationRequest, DIRECT_EXECUTOR, this);
+ protected boolean registerWithService(LocationRequest locationRequest,
+ Collection<GeofenceRegistration> registrations) {
+ getLocationManager().requestLocationUpdates(FUSED_PROVIDER, locationRequest,
+ DIRECT_EXECUTOR, this);
return true;
}
@@ -385,7 +394,7 @@
}
@Override
- protected LocationRequest mergeRequests(Collection<GeofenceRegistration> registrations) {
+ protected LocationRequest mergeRegistrations(Collection<GeofenceRegistration> registrations) {
Location location = getLastLocation();
long realtimeMs = SystemClock.elapsedRealtime();
@@ -417,13 +426,11 @@
intervalMs = mSettingsHelper.getBackgroundThrottleProximityAlertIntervalMs();
}
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(
- LocationManager.FUSED_PROVIDER, intervalMs, 0, false);
- request.setFastestInterval(0);
- request.setHideFromAppOps(true);
- request.setWorkSource(workSource);
-
- return request;
+ return new LocationRequest.Builder(intervalMs)
+ .setMinUpdateIntervalMillis(0)
+ .setHiddenFromAppOps(true)
+ .setWorkSource(workSource)
+ .build();
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
index 5ddc6a1..b99e269 100644
--- a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
@@ -28,6 +28,7 @@
import com.android.internal.util.Preconditions;
import com.android.server.location.util.Injector;
+import java.util.Collection;
import java.util.List;
/**
@@ -59,7 +60,8 @@
}
@Override
- protected boolean registerWithService(Void ignored) {
+ protected boolean registerWithService(Void ignored,
+ Collection<GnssListenerRegistration> registrations) {
Preconditions.checkState(mNative.isAntennaInfoSupported());
if (mNative.startAntennaInfoListening()) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
index 14ab79e..890f51b 100644
--- a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
+++ b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
@@ -200,7 +200,7 @@
}
/**
- * Updates the GNSS HAL satellite blacklist.
+ * Updates the GNSS HAL satellite denylist.
*/
void setSatelliteBlacklist(int[] constellations, int[] svids) {
native_set_satellite_blacklist(constellations, svids);
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 a9fdacc..a830e2f 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -26,11 +26,13 @@
import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.util.identity.CallerIdentity;
import android.os.Binder;
+import android.os.Build;
import android.os.IBinder;
import android.os.IInterface;
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;
@@ -43,6 +45,7 @@
import com.android.server.location.util.UserInfoHelper.UserListener;
import java.io.PrintWriter;
+import java.util.Collection;
import java.util.Objects;
/**
@@ -52,14 +55,15 @@
* Listeners must be registered with the associated IBinder as the key, if the IBinder dies, the
* registration will automatically be removed.
*
- * @param <TRequest> request type
- * @param <TListener> listener type
- * @param <TMergedRequest> merged request type
+ * @param <TRequest> request type
+ * @param <TListener> listener type
+ * @param <TMergedRegistration> merged registration type
*/
public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInterface,
- TMergedRequest> extends
- ListenerMultiplexer<IBinder, TRequest, TListener, GnssListenerMultiplexer<TRequest,
- TListener, TMergedRequest>.GnssListenerRegistration, TMergedRequest> {
+ TMergedRegistration> extends
+ ListenerMultiplexer<IBinder, TListener, ListenerOperation<TListener>,
+ GnssListenerMultiplexer<TRequest, TListener, TMergedRegistration>
+ .GnssListenerRegistration, TMergedRegistration> {
/**
* Registration object for GNSS listeners.
@@ -74,11 +78,11 @@
protected GnssListenerRegistration(@Nullable TRequest request,
CallerIdentity callerIdentity, TListener listener) {
- super(TAG, request, callerIdentity, listener);
+ super(request, callerIdentity, listener);
}
@Override
- protected GnssListenerMultiplexer<TRequest, TListener, TMergedRequest> getOwner() {
+ protected GnssListenerMultiplexer<TRequest, TListener, TMergedRegistration> getOwner() {
return GnssListenerMultiplexer.this;
}
@@ -199,6 +203,11 @@
LocalServices.getService(LocationManagerInternal.class));
}
+ @Override
+ public String getTag() {
+ return TAG;
+ }
+
/**
* May be overridden by subclasses to return whether the service is supported or not. This value
* should never change for the lifetime of the multiplexer. If the service is unsupported, all
@@ -271,6 +280,21 @@
return mLocationManagerInternal.isProvider(null, identity);
}
+ // this provides a default implementation for all further subclasses which assumes that there is
+ // never an associated request object, and thus nothing interesting to merge. the majority of
+ // gnss listener multiplexers do not current have associated requests, and the ones that do can
+ // override this implementation.
+ protected TMergedRegistration mergeRegistrations(
+ Collection<GnssListenerRegistration> gnssListenerRegistrations) {
+ if (Build.IS_DEBUGGABLE) {
+ for (GnssListenerRegistration registration : gnssListenerRegistrations) {
+ Preconditions.checkState(registration.getRequest() == null);
+ }
+ }
+
+ return null;
+ }
+
@Override
protected void onRegister() {
if (!isServiceSupported()) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index a4486d7..f879b19 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -45,6 +45,7 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
@@ -273,7 +274,7 @@
// if the fix interval is below this we leave GPS on,
// if above then we cycle the GPS driver.
- // Typical hot TTTF is ~5 seconds, so 10 seconds seems sane.
+ // Typical hot TTTF is ~5 seconds, so 10 seconds seems valid.
private static final int GPS_POLLING_THRESHOLD_INTERVAL = 10 * 1000;
// how long to wait if we have a network error in NTP or PSDS downloading
@@ -700,9 +701,8 @@
Context.LOCATION_SERVICE);
String provider;
LocationChangeListener locationListener;
- LocationRequest locationRequest = new LocationRequest()
- .setInterval(LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS)
- .setFastestInterval(LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS);
+ LocationRequest.Builder locationRequest = new LocationRequest.Builder(
+ LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS);
if (independentFromGnss) {
// For fast GNSS TTFF
@@ -716,8 +716,6 @@
locationRequest.setQuality(LocationRequest.ACCURACY_FINE);
}
- locationRequest.setProvider(provider);
-
// Ignore location settings if in emergency mode. This is only allowed for
// isUserEmergency request (introduced in HAL v2.0), or HAL v1.1.
if (mNIHandler.getInEmergency()) {
@@ -735,8 +733,8 @@
provider, durationMillis));
try {
- locationManager.requestLocationUpdates(locationRequest,
- locationListener, mHandler.getLooper());
+ locationManager.requestLocationUpdates(provider, locationRequest.build(),
+ new HandlerExecutor(mHandler), locationListener);
locationListener.mNumLocationUpdateRequest++;
mHandler.postDelayed(() -> {
if (--locationListener.mNumLocationUpdateRequest == 0) {
@@ -958,8 +956,9 @@
enabled &= !mDisableGpsForPowerManager;
// .. but enable anyway, if there's an active settings-ignored request (e.g. ELS)
- enabled |= (mProviderRequest != null && mProviderRequest.reportLocation
- && mProviderRequest.locationSettingsIgnored);
+ enabled |= (mProviderRequest != null
+ && mProviderRequest.isActive()
+ && mProviderRequest.isLocationSettingsIgnored());
// ... and, finally, disable anyway, if device is being shut down
enabled &= !mShutdown;
@@ -994,20 +993,20 @@
// Called when the requirements for GPS may have changed
private void updateRequirements() {
- if (mProviderRequest == null || mProviderRequest.workSource == null) {
+ if (mProviderRequest == null || mProviderRequest.getWorkSource() == null) {
return;
}
if (DEBUG) Log.d(TAG, "setRequest " + mProviderRequest);
- if (mProviderRequest.reportLocation && isGpsEnabled()) {
+ if (mProviderRequest.isActive() && isGpsEnabled()) {
// update client uids
- updateClientUids(mProviderRequest.workSource);
+ updateClientUids(mProviderRequest.getWorkSource());
- mFixInterval = (int) mProviderRequest.interval;
- mLowPowerMode = mProviderRequest.lowPowerMode;
+ mFixInterval = (int) mProviderRequest.getIntervalMillis();
+ mLowPowerMode = mProviderRequest.isLowPower();
// check for overflow
- if (mFixInterval != mProviderRequest.interval) {
- Log.w(TAG, "interval overflow: " + mProviderRequest.interval);
+ if (mFixInterval != mProviderRequest.getIntervalMillis()) {
+ Log.w(TAG, "interval overflow: " + mProviderRequest.getIntervalMillis());
mFixInterval = Integer.MAX_VALUE;
}
@@ -1207,7 +1206,7 @@
}
int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000);
- mLowPowerMode = mProviderRequest.lowPowerMode;
+ mLowPowerMode = mProviderRequest.isLowPower();
if (!setPositionMode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
interval, 0, 0, mLowPowerMode)) {
setStarted(false);
@@ -1952,20 +1951,13 @@
// listen for PASSIVE_PROVIDER updates
LocationManager locManager =
(LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
- long minTime = 0;
- float minDistance = 0;
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(
- LocationManager.PASSIVE_PROVIDER,
- minTime,
- minDistance,
- false);
- // Don't keep track of this request since it's done on behalf of other clients
- // (which are kept track of separately).
- request.setHideFromAppOps(true);
locManager.requestLocationUpdates(
- request,
- new NetworkLocationListener(),
- getLooper());
+ LocationManager.PASSIVE_PROVIDER,
+ new LocationRequest.Builder(0)
+ .setHiddenFromAppOps(true)
+ .build(),
+ new HandlerExecutor(this),
+ new NetworkLocationListener());
updateEnabled();
}
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 0815d46..2faa15f 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -76,7 +76,8 @@
}
@Override
- protected boolean registerWithService(Boolean fullTrackingRequest) {
+ protected boolean registerWithService(Boolean fullTrackingRequest,
+ Collection<GnssListenerRegistration> registrations) {
Preconditions.checkState(mNative.isMeasurementSupported());
if (mNative.startMeasurementCollection(fullTrackingRequest)) {
@@ -121,7 +122,7 @@
}
@Override
- protected Boolean mergeRequests(Collection<GnssListenerRegistration> registrations) {
+ protected Boolean mergeRegistrations(Collection<GnssListenerRegistration> registrations) {
if (mSettingsHelper.isGnssMeasurementsFullTrackingEnabled()) {
return true;
}
@@ -141,11 +142,11 @@
LocationStatsEnums.USAGE_STARTED,
LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER,
registration.getIdentity().getPackageName(),
- /* LocationRequest= */ null,
- /* hasListener= */ true,
- /* hasIntent= */ false,
- /* geofence= */ null,
- registration.isForeground());
+ null,
+ null,
+ true,
+ false,
+ null, registration.isForeground());
}
@Override
@@ -154,11 +155,11 @@
LocationStatsEnums.USAGE_ENDED,
LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER,
registration.getIdentity().getPackageName(),
- /* LocationRequest= */ null,
- /* hasListener= */ true,
- /* hasIntent= */ false,
- /* geofence= */ null,
- registration.isForeground());
+ null,
+ null,
+ true,
+ false,
+ null, registration.isForeground());
}
/**
diff --git a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
index 7dcffc6..861abc3 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
@@ -30,6 +30,8 @@
import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
+import java.util.Collection;
+
/**
* An base implementation for GPS navigation messages provider.
* It abstracts out the responsibility of handling listeners, while still allowing technology
@@ -61,7 +63,8 @@
}
@Override
- protected boolean registerWithService(Void ignored) {
+ protected boolean registerWithService(Void ignored,
+ Collection<GnssListenerRegistration> registrations) {
Preconditions.checkState(mNative.isNavigationMessageSupported());
if (mNative.startNavigationMessageCollection()) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
index cea5a69..2227506 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
@@ -518,7 +518,7 @@
if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
if (apn == null) {
- // assign a dummy value in the case of C2K as otherwise we will have a runtime
+ // assign a placeholder value in the case of C2K as otherwise we will have a runtime
// exception in the following call to native_agps_data_conn_open
apn = "dummy-apn";
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssSatelliteBlacklistHelper.java b/services/core/java/com/android/server/location/gnss/GnssSatelliteBlacklistHelper.java
index dccef9b..426ce8c 100644
--- a/services/core/java/com/android/server/location/gnss/GnssSatelliteBlacklistHelper.java
+++ b/services/core/java/com/android/server/location/gnss/GnssSatelliteBlacklistHelper.java
@@ -31,7 +31,7 @@
import java.util.List;
/**
- * Detects blacklist change and updates the blacklist.
+ * Detects denylist change and updates the denylist.
*/
class GnssSatelliteBlacklistHelper {
diff --git a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
index 19f7927..248d38a 100644
--- a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
@@ -31,6 +31,8 @@
import com.android.server.location.util.Injector;
import com.android.server.location.util.LocationUsageLogger;
+import java.util.Collection;
+
/**
* Implementation of a handler for {@link IGnssStatusListener}.
*/
@@ -51,7 +53,8 @@
}
@Override
- protected boolean registerWithService(Void ignored) {
+ protected boolean registerWithService(Void ignored,
+ Collection<GnssListenerRegistration> registrations) {
if (D) {
Log.d(TAG, "starting gnss status");
}
@@ -71,11 +74,11 @@
LocationStatsEnums.USAGE_STARTED,
LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
registration.getIdentity().getPackageName(),
- /* LocationRequest= */ null,
- /* hasListener= */ true,
- /* hasIntent= */ false,
- /* geofence= */ null,
- registration.isForeground());
+ null,
+ null,
+ true,
+ false,
+ null, registration.isForeground());
}
@Override
@@ -84,10 +87,11 @@
LocationStatsEnums.USAGE_ENDED,
LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
registration.getIdentity().getPackageName(),
- /* LocationRequest= */ null,
- /* hasListener= */ true,
- /* hasIntent= */ false,
- /* geofence= */ null,
+ null,
+ null,
+ true,
+ false,
+ null,
registration.isForeground());
}
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 58aabda..bc675ce 100644
--- a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
@@ -23,6 +23,8 @@
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
@@ -32,7 +34,8 @@
* @param <TListener> listener type
*/
public abstract class BinderListenerRegistration<TRequest, TListener> extends
- RemoteListenerRegistration<TRequest, TListener> implements Binder.DeathRecipient {
+ RemoteListenerRegistration<TRequest, TListener, ListenerOperation<TListener>> implements
+ Binder.DeathRecipient {
/**
* Interface to allow binder retrieval when keys are not themselves IBinders.
@@ -44,9 +47,9 @@
IBinder getBinder();
}
- protected BinderListenerRegistration(String tag, @Nullable TRequest request,
- CallerIdentity callerIdentity, TListener listener) {
- super(tag, request, callerIdentity, listener);
+ protected BinderListenerRegistration(@Nullable TRequest request, CallerIdentity callerIdentity,
+ TListener listener) {
+ super(request, callerIdentity, listener);
}
@Override
@@ -78,10 +81,20 @@
protected void onBinderListenerUnregister() {}
@Override
+ public void onOperationFailure(ListenerOperation<TListener> operation, Exception e) {
+ if (e instanceof RemoteException) {
+ Log.w(getOwner().getTag(), "registration " + this + " removed", e);
+ remove();
+ } else {
+ super.onOperationFailure(operation, e);
+ }
+ }
+
+ @Override
public void binderDied() {
try {
- if (Log.isLoggable(mTag, Log.DEBUG)) {
- Log.d(mTag, "binder registration " + getIdentity() + " died");
+ if (Log.isLoggable(getOwner().getTag(), Log.DEBUG)) {
+ Log.d(getOwner().getTag(), "binder registration " + getIdentity() + " died");
}
remove();
} catch (RuntimeException e) {
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 8a6b8aa..87d668a 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
@@ -37,12 +37,20 @@
import java.util.function.Predicate;
/**
- * A base class to multiplex client listener registrations within system server. Registrations are
- * divided into two categories, active registrations and inactive registrations, as defined by
- * {@link #isActive(ListenerRegistration)}. If a registration's active state changes,
+ * A base class to multiplex client listener registrations within system server. Every listener is
+ * represented by a registration object which stores all required state for a listener. Keys are
+ * used to uniquely identify every registration. Listener operations may be executed on
+ * registrations in order to invoke the represented listener.
+ *
+ * Registrations are divided into two categories, active registrations and inactive registrations,
+ * as defined by {@link #isActive(ListenerRegistration)}. If a registration's active state changes,
* {@link #updateRegistrations(Predicate)} must be invoked and return true for any registration
* whose active state may have changed. Listeners will only be invoked for active registrations.
*
+ * The set of active registrations is combined into a single merged registration, which is submitted
+ * to the backing service when necessary in order to register the service. The merged registration
+ * is updated whenever the set of active registration changes.
+ *
* Callbacks invoked for various changes will always be ordered according to this lifecycle list:
*
* <ul>
@@ -63,14 +71,16 @@
* {@link #removeRegistration(Object, ListenerRegistration)}, not via any other removal method. This
* ensures re-entrant removal does not accidentally remove the incorrect registration.
*
- * @param <TKey> key type
- * @param <TRequest> request type
- * @param <TListener> listener type
- * @param <TRegistration> registration type
- * @param <TMergedRequest> merged request type
+ * @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, TRequest, TListener,
- TRegistration extends ListenerRegistration<TRequest, TListener>, TMergedRequest> {
+public abstract class ListenerMultiplexer<TKey, TListener,
+ TListenerOperation extends ListenerOperation<TListener>,
+ TRegistration extends ListenerRegistration<TListener, TListenerOperation>,
+ TMergedRegistration> {
@GuardedBy("mRegistrations")
private final ArrayMap<TKey, TRegistration> mRegistrations = new ArrayMap<>();
@@ -88,25 +98,42 @@
private boolean mServiceRegistered = false;
@GuardedBy("mRegistrations")
- private TMergedRequest mCurrentRequest;
+ @Nullable private TMergedRegistration mMerged;
/**
- * Should be implemented to register with the backing service with the given merged request, and
- * should return true if a matching call to {@link #unregisterWithService()} is required to
- * unregister (ie, if registration succeeds).
+ * Should be implemented to return a unique identifying tag that may be used for logging, etc...
+ */
+ public abstract @NonNull String getTag();
+
+ /**
+ * Should be implemented to register with the backing service with the given merged
+ * registration, and should return true if a matching call to {@link #unregisterWithService()}
+ * is required to unregister (ie, if registration succeeds). The set of registrations passed in
+ * is the same set passed into {@link #mergeRegistrations(Collection)} to generate the merged
+ * registration.
*
- * @see #reregisterWithService(Object, Object)
+ * <p class="note">It may seem redundant to pass in the set of active registrations when they
+ * have already been used to generate the merged request, and indeed, for many implementations
+ * this parameter can likely simply be ignored. However, some implementations may require access
+ * to the set of registrations used to generate the merged requestion for further logic even
+ * after the merged registration has been generated.
+ *
+ * @see #mergeRegistrations(Collection)
+ * @see #reregisterWithService(Object, Object, Collection)
*/
- protected abstract boolean registerWithService(TMergedRequest newRequest);
+ protected abstract boolean registerWithService(TMergedRegistration merged,
+ @NonNull Collection<TRegistration> registrations);
/**
- * Invoked when the service already has a request, and it is being replaced with a new request.
- * The default implementation unregisters first, then registers with the new merged request, but
- * this may be overridden by subclasses in order to reregister more efficiently.
+ * Invoked when the service has already been registered with some merged registration, and is
+ * now being registered with a different merged registration. The default implementation simply
+ * invokes {@link #registerWithService(Object, Collection)}.
+ *
+ * @see #registerWithService(Object, Collection)
*/
- protected boolean reregisterWithService(TMergedRequest oldRequest, TMergedRequest newRequest) {
- unregisterWithService();
- return registerWithService(newRequest);
+ protected boolean reregisterWithService(TMergedRegistration oldMerged,
+ TMergedRegistration newMerged, @NonNull Collection<TRegistration> registrations) {
+ return registerWithService(newMerged, registrations);
}
/**
@@ -116,28 +143,23 @@
/**
* Defines whether a registration is currently active or not. Only active registrations will be
- * considered within {@link #mergeRequests(Collection)} to calculate the merged request, and
- * listener invocations will only be delivered to active requests. If a registration's active
- * state changes, {@link #updateRegistrations(Predicate)} must be invoked with a function that
- * returns true for any registrations that may have changed their active state.
+ * forwarded to {@link #registerWithService(Object, Collection)}, and listener invocations will
+ * only be delivered to active requests. If a registration's active state changes,
+ * {@link #updateRegistrations(Predicate)} must be invoked with a function that returns true for
+ * any registrations that may have changed their active state.
*/
protected abstract boolean isActive(@NonNull TRegistration registration);
/**
- * Called in order to generate a merged request from the given registrations. The list of
- * registrations will never be empty.
+ * Called in order to generate a merged registration from the given set of active registrations.
+ * The list of registrations will never be empty. If the resulting merged registration is equal
+ * to the currently registered merged registration, nothing further will happen. If the merged
+ * registration differs, {@link #registerWithService(Object, Collection)} or
+ * {@link #reregisterWithService(Object, Object, Collection)} will be invoked with the new
+ * merged registration so that the backing service can be updated.
*/
- protected @Nullable TMergedRequest mergeRequests(
- @NonNull Collection<TRegistration> registrations) {
- if (Build.IS_DEBUGGABLE) {
- for (TRegistration registration : registrations) {
- // if using non-null requests then implementations must override this method
- Preconditions.checkState(registration.getRequest() == null);
- }
- }
-
- return null;
- }
+ protected abstract @Nullable TMergedRegistration mergeRegistrations(
+ @NonNull Collection<TRegistration> registrations);
/**
* Invoked when the multiplexer goes from having no registrations to having some registrations.
@@ -348,41 +370,42 @@
}
if (actives.isEmpty()) {
- mCurrentRequest = null;
if (mServiceRegistered) {
+ mMerged = null;
mServiceRegistered = false;
- mCurrentRequest = null;
unregisterWithService();
}
return;
}
- TMergedRequest merged = mergeRequests(actives);
- if (!mServiceRegistered || !Objects.equals(merged, mCurrentRequest)) {
+ TMergedRegistration merged = mergeRegistrations(actives);
+ if (!mServiceRegistered || !Objects.equals(merged, mMerged)) {
if (mServiceRegistered) {
- mServiceRegistered = reregisterWithService(mCurrentRequest, merged);
+ mServiceRegistered = reregisterWithService(mMerged, merged, actives);
} else {
- mServiceRegistered = registerWithService(merged);
+ mServiceRegistered = registerWithService(merged, actives);
}
- if (mServiceRegistered) {
- mCurrentRequest = merged;
- } else {
- mCurrentRequest = null;
- }
+ mMerged = mServiceRegistered ? merged : null;
}
}
}
/**
- * Clears currently stored service state, and invokes {@link #updateService()} to force a new
- * call to {@link #registerWithService(Object)} if necessary. This is useful, for instance, if
- * the backing service has crashed or otherwise lost state, and needs to be re-initialized.
+ * If the service is currently registered, unregisters it and then calls
+ * {@link #updateService()} so that {@link #registerWithService(Object, Collection)} will be
+ * re-invoked. This is useful, for instance, if the backing service has crashed or otherwise
+ * lost state, and needs to be re-initialized. Because this unregisters first, this is safe to
+ * use even if there is a possibility the backing server has not crashed, or has already been
+ * reinitialized.
*/
protected final void resetService() {
synchronized (mRegistrations) {
- mServiceRegistered = false;
- mCurrentRequest = null;
- updateService();
+ if (mServiceRegistered) {
+ mMerged = null;
+ mServiceRegistered = false;
+ unregisterWithService();
+ updateService();
+ }
}
}
@@ -435,12 +458,12 @@
if (++mActiveRegistrationsCount == 1) {
onActive();
}
- ListenerOperation<TListener> operation = registration.onActive();
+ TListenerOperation operation = registration.onActive();
if (operation != null) {
execute(registration, operation);
}
} else {
- ListenerOperation<TListener> operation = registration.onInactive();
+ TListenerOperation operation = registration.onInactive();
if (operation != null) {
execute(registration, operation);
}
@@ -459,14 +482,14 @@
* change the active state of the registration.
*/
protected final void deliverToListeners(
- @NonNull Function<TRegistration, ListenerOperation<TListener>> function) {
+ @NonNull Function<TRegistration, TListenerOperation> 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()) {
- ListenerOperation<TListener> operation = function.apply(registration);
+ TListenerOperation operation = function.apply(registration);
if (operation != null) {
execute(registration, operation);
}
@@ -483,7 +506,7 @@
* deliverToListeners(registration -> operation);
* </pre>
*/
- protected final void deliverToListeners(@NonNull ListenerOperation<TListener> operation) {
+ protected final void deliverToListeners(@NonNull TListenerOperation operation) {
synchronized (mRegistrations) {
try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
final int size = mRegistrations.size();
@@ -502,7 +525,7 @@
onRegistrationActiveChanged(registration);
}
- private void execute(TRegistration registration, ListenerOperation<TListener> operation) {
+ private void execute(TRegistration registration, TListenerOperation operation) {
registration.executeInternal(operation);
}
@@ -539,10 +562,11 @@
*/
protected void dumpServiceState(PrintWriter pw) {
if (mServiceRegistered) {
- if (mCurrentRequest == null) {
- pw.print("registered");
- } else {
- pw.print("registered with " + mCurrentRequest);
+ pw.print("registered");
+ if (mMerged != null) {
+ pw.print(" [");
+ pw.print(mMerged);
+ pw.print("]");
}
} else {
pw.print("unregistered");
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 deb9660..d7ecbcb 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
@@ -21,6 +21,7 @@
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;
@@ -29,22 +30,21 @@
* A listener registration object which holds data associated with the listener, such as an optional
* request, and an executor responsible for listener invocations.
*
- * @param <TRequest> request type
- * @param <TListener> listener type
+ * @param <TListener> listener type
+ * @param <TListenerOperation> listener operation type
*/
-public class ListenerRegistration<TRequest, TListener> implements ListenerExecutor {
+public class ListenerRegistration<TListener,
+ TListenerOperation extends ListenerOperation<TListener>> implements
+ ListenerExecutor {
private final Executor mExecutor;
- private final @Nullable TRequest mRequest;
private boolean mActive;
private volatile @Nullable TListener mListener;
- protected ListenerRegistration(Executor executor, @Nullable TRequest request,
- TListener listener) {
+ protected ListenerRegistration(Executor executor, TListener listener) {
mExecutor = Objects.requireNonNull(executor);
- mRequest = request;
mActive = false;
mListener = Objects.requireNonNull(listener);
}
@@ -54,13 +54,6 @@
}
/**
- * Returns the request associated with this listener, or null if one wasn't supplied.
- */
- public @Nullable TRequest getRequest() {
- return mRequest;
- }
-
- /**
* May be overridden by subclasses. Invoked when registration occurs. Invoked while holding the
* owning multiplexer's internal lock.
*/
@@ -77,7 +70,7 @@
* returns a non-null operation, that operation will be invoked for the listener. Invoked
* while holding the owning multiplexer's internal lock.
*/
- protected @Nullable ListenerOperation<TListener> onActive() {
+ protected @Nullable TListenerOperation onActive() {
return null;
}
@@ -86,7 +79,7 @@
* a non-null operation, that operation will be invoked for the listener. Invoked while holding
* the owning multiplexer's internal lock.
*/
- protected @Nullable ListenerOperation<TListener> onInactive() {
+ protected @Nullable TListenerOperation onInactive() {
return null;
}
@@ -115,21 +108,38 @@
/**
* May be overridden by subclasses, however should rarely be needed. Invoked when the listener
* associated with this registration is unregistered, which may occur before the registration
- * itself is unregistered. This immediately prevents the listener from being further invoked.
+ * itself is unregistered. This immediately prevents the listener from being further invoked
+ * until the registration itself can be finalized and unregistered completely.
*/
- protected void onListenerUnregister() {};
+ protected void onListenerUnregister() {}
- final void executeInternal(@NonNull ListenerOperation<TListener> operation) {
- executeSafely(mExecutor, () -> mListener, operation);
+ /**
+ * 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);
+ }
+
+ final void executeInternal(@NonNull TListenerOperation operation) {
+ executeSafely(mExecutor, () -> mListener,
+ onExecuteOperation(Objects.requireNonNull(operation)), this::onOperationFailure);
}
@Override
public String toString() {
- if (mRequest == null) {
- return "[]";
- } else {
- return mRequest.toString();
- }
+ return "[]";
}
@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 7b6154e..e57b532 100644
--- a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
@@ -21,6 +21,8 @@
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
@@ -30,7 +32,8 @@
* @param <TListener> listener type
*/
public abstract class PendingIntentListenerRegistration<TRequest, TListener> extends
- RemoteListenerRegistration<TRequest, TListener> implements PendingIntent.CancelListener {
+ RemoteListenerRegistration<TRequest, TListener, ListenerOperation<TListener>> implements
+ PendingIntent.CancelListener {
/**
* Interface to allowed pending intent retrieval when keys are not themselves PendingIntents.
@@ -42,9 +45,9 @@
PendingIntent getPendingIntent();
}
- protected PendingIntentListenerRegistration(String tag, @Nullable TRequest request,
+ protected PendingIntentListenerRegistration(@Nullable TRequest request,
CallerIdentity callerIdentity, TListener listener) {
- super(tag, request, callerIdentity, listener);
+ super(request, callerIdentity, listener);
}
@Override
@@ -70,9 +73,20 @@
protected void onPendingIntentListenerUnregister() {}
@Override
+ public void onOperationFailure(ListenerOperation<TListener> operation, Exception e) {
+ if (e instanceof PendingIntent.CanceledException) {
+ Log.w(getOwner().getTag(), "registration " + this + " removed", e);
+ remove();
+ } else {
+ super.onOperationFailure(operation, e);
+ }
+ }
+
+ @Override
public void onCancelled(PendingIntent intent) {
- if (Log.isLoggable(mTag, Log.DEBUG)) {
- Log.d(mTag, "pending intent registration " + getIdentity() + " canceled");
+ if (Log.isLoggable(getOwner().getTag(), Log.DEBUG)) {
+ Log.d(getOwner().getTag(),
+ "pending intent registration " + getIdentity() + " canceled");
}
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 e4b0b19..242bf32 100644
--- a/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java
@@ -24,6 +24,7 @@
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;
@@ -35,11 +36,13 @@
* invocation should already be asynchronous. Listeners from the same process will be run on a
* normal executor, since in-process listener invocation may be synchronous.
*
- * @param <TRequest> request type
- * @param <TListener> listener type
+ * @param <TRequest> request type
+ * @param <TListener> listener type
+ * @param <TListenerOperation> listener operation type
*/
-public abstract class RemoteListenerRegistration<TRequest, TListener> extends
- RemovableListenerRegistration<TRequest, TListener> {
+public abstract class RemoteListenerRegistration<TRequest, TListener,
+ TListenerOperation extends ListenerOperation<TListener>> extends
+ RemovableListenerRegistration<TRequest, TListener, TListenerOperation> {
@VisibleForTesting
public static final Executor IN_PROCESS_EXECUTOR = FgThread.getExecutor();
@@ -59,9 +62,9 @@
private final CallerIdentity mIdentity;
- protected RemoteListenerRegistration(String tag, @Nullable TRequest request,
- CallerIdentity identity, TListener listener) {
- super(tag, chooseExecutor(identity), request, listener);
+ protected RemoteListenerRegistration(@Nullable TRequest request, CallerIdentity identity,
+ TListener listener) {
+ super(chooseExecutor(identity), request, listener);
mIdentity = Objects.requireNonNull(identity);
}
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 2383bec..d3b5f66 100644
--- a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
@@ -17,7 +17,8 @@
package com.android.server.location.listeners;
import android.annotation.Nullable;
-import android.util.Log;
+
+import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -26,20 +27,19 @@
* A listener registration that stores its own key, and thus can remove itself. By default it will
* remove itself if any checked exception occurs on listener execution.
*
- * @param <TRequest> request type
- * @param <TListener> listener type
+ * @param <TRequest> request type
+ * @param <TListener> listener type
+ * @param <TListenerOperation> listener operation type
*/
-public abstract class RemovableListenerRegistration<TRequest, TListener> extends
- ListenerRegistration<TRequest, TListener> {
-
- protected final String mTag;
+public abstract class RemovableListenerRegistration<TRequest, TListener,
+ TListenerOperation extends ListenerOperation<TListener>> extends
+ RequestListenerRegistration<TRequest, TListener, TListenerOperation> {
private volatile @Nullable Object mKey;
- protected RemovableListenerRegistration(String tag, Executor executor,
- @Nullable TRequest request, TListener listener) {
+ protected RemovableListenerRegistration(Executor executor, @Nullable TRequest request,
+ TListener listener) {
super(executor, request, listener);
- mTag = Objects.requireNonNull(tag);
}
/**
@@ -47,7 +47,8 @@
* 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 TRequest, ? super TListener, ?, ?> getOwner();
+ protected abstract ListenerMultiplexer<?, ? super TListener, ?
+ super TListenerOperation, ?, ?> getOwner();
/**
* Returns the key associated with this registration. May not be invoked before
@@ -69,12 +70,6 @@
}
@Override
- public <Listener> void onOperationFailure(ListenerOperation<Listener> operation, Exception e) {
- Log.w(mTag, "registration " + this + " removed due to unexpected exception", e);
- remove();
- }
-
- @Override
protected final void onRegister(Object key) {
mKey = Objects.requireNonNull(key);
onRemovableListenerRegister();
diff --git a/services/core/java/com/android/server/location/listeners/RequestListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RequestListenerRegistration.java
new file mode 100644
index 0000000..d97abae
--- /dev/null
+++ b/services/core/java/com/android/server/location/listeners/RequestListenerRegistration.java
@@ -0,0 +1,58 @@
+/*
+ * 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.location.listeners;
+
+import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
+
+import java.util.concurrent.Executor;
+
+/**
+ * A listener registration object which includes an associated request.
+ *
+ * @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> {
+
+ private final TRequest mRequest;
+
+ protected RequestListenerRegistration(Executor executor, TRequest request,
+ TListener listener) {
+ super(executor, listener);
+ mRequest = request;
+ }
+
+ /**
+ * Returns the request associated with this listener, or null if one wasn't supplied.
+ */
+ public TRequest getRequest() {
+ return mRequest;
+ }
+
+ @Override
+ public String toString() {
+ if (mRequest == null) {
+ return "[]";
+ } else {
+ return mRequest.toString();
+ }
+ }
+}
+
diff --git a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
new file mode 100644
index 0000000..92dabe3
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
@@ -0,0 +1,214 @@
+/*
+ * 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.location.timezone;
+
+import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.location.timezone.LocationTimeZoneEvent;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+
+import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;
+
+import java.util.Objects;
+
+/**
+ * The real, system-server side implementation of a binder call backed {@link
+ * LocationTimeZoneProvider}. It handles keeping track of current state, timeouts and ensuring
+ * events are passed to the {@link LocationTimeZoneProviderController} on the required thread.
+ */
+class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
+
+ private static final String TAG = LocationTimeZoneManagerService.TAG;
+
+ @NonNull private final LocationTimeZoneProviderProxy mProxy;
+
+ BinderLocationTimeZoneProvider(
+ @NonNull ThreadingDomain threadingDomain,
+ @NonNull String providerName,
+ @NonNull LocationTimeZoneProviderProxy proxy) {
+ super(threadingDomain, providerName);
+ mProxy = Objects.requireNonNull(proxy);
+ }
+
+ @Override
+ void onInitialize() {
+ mProxy.setListener(new LocationTimeZoneProviderProxy.Listener() {
+ @Override
+ public void onReportLocationTimeZoneEvent(
+ @NonNull LocationTimeZoneEvent locationTimeZoneEvent) {
+ handleLocationTimeZoneEvent(locationTimeZoneEvent);
+ }
+
+ @Override
+ public void onProviderBound() {
+ handleOnProviderBound();
+ }
+
+ @Override
+ public void onProviderUnbound() {
+ handleProviderLost("onProviderUnbound()");
+ }
+ });
+ }
+
+ private void handleProviderLost(String reason) {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ ProviderState currentState = mCurrentState.get();
+ switch (currentState.stateEnum) {
+ case PROVIDER_STATE_ENABLED: {
+ // Losing a remote provider is treated as becoming uncertain.
+ String msg = "handleProviderLost reason=" + reason
+ + ", mProviderName=" + mProviderName
+ + ", currentState=" + currentState;
+ debugLog(msg);
+ // This is an unusual PROVIDER_STATE_ENABLED state because event == null
+ ProviderState newState = currentState.newState(
+ PROVIDER_STATE_ENABLED, null, currentState.currentUserConfiguration,
+ msg);
+ setCurrentState(newState, true);
+ break;
+ }
+ case PROVIDER_STATE_DISABLED: {
+ debugLog("handleProviderLost reason=" + reason
+ + ", mProviderName=" + mProviderName
+ + ", currentState=" + currentState
+ + ": No state change required, provider is disabled.");
+ break;
+ }
+ case PROVIDER_STATE_PERM_FAILED: {
+ debugLog("handleProviderLost reason=" + reason
+ + ", mProviderName=" + mProviderName
+ + ", currentState=" + currentState
+ + ": No state change required, provider is perm failed.");
+ break;
+ }
+ default: {
+ throw new IllegalStateException("Unknown currentState=" + currentState);
+ }
+ }
+ }
+ }
+
+ private void handleOnProviderBound() {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ ProviderState currentState = mCurrentState.get();
+ switch (currentState.stateEnum) {
+ case PROVIDER_STATE_ENABLED: {
+ debugLog("handleOnProviderBound mProviderName=" + mProviderName
+ + ", currentState=" + currentState + ": Provider is enabled.");
+ break;
+ }
+ case PROVIDER_STATE_DISABLED: {
+ debugLog("handleOnProviderBound mProviderName=" + mProviderName
+ + ", currentState=" + currentState + ": Provider is disabled.");
+ break;
+ }
+ case PROVIDER_STATE_PERM_FAILED: {
+ debugLog("handleOnProviderBound"
+ + ", mProviderName=" + mProviderName
+ + ", currentState=" + currentState
+ + ": No state change required, provider is perm failed.");
+ break;
+ }
+ default: {
+ throw new IllegalStateException("Unknown currentState=" + currentState);
+ }
+ }
+ }
+ }
+
+ @Override
+ void onEnable() {
+ // Set a request on the proxy - it will be sent immediately if the service is bound,
+ // or will be sent as soon as the service becomes bound.
+ // TODO(b/152744911): Decide whether to send a timeout so the provider knows how long
+ // it has to generate the first event before it could be bypassed.
+ LocationTimeZoneProviderRequest request =
+ new LocationTimeZoneProviderRequest.Builder()
+ .setReportLocationTimeZone(true)
+ .build();
+ mProxy.setRequest(request);
+ }
+
+ @Override
+ void onDisable() {
+ LocationTimeZoneProviderRequest request =
+ new LocationTimeZoneProviderRequest.Builder()
+ .setReportLocationTimeZone(false)
+ .build();
+ mProxy.setRequest(request);
+ }
+
+ @Override
+ void logWarn(String msg) {
+ Slog.w(TAG, msg);
+ }
+
+ @Override
+ public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
+ synchronized (mSharedLock) {
+ ipw.println("{BinderLocationTimeZoneProvider}");
+ ipw.println("mProviderName=" + mProviderName);
+ ipw.println("mCurrentState=" + mCurrentState);
+ ipw.println("mProxy=" + mProxy);
+
+ ipw.println("State history:");
+ ipw.increaseIndent();
+ mCurrentState.dump(ipw);
+ ipw.decreaseIndent();
+
+ ipw.println("Proxy details:");
+ ipw.increaseIndent();
+ mProxy.dump(ipw, args);
+ ipw.decreaseIndent();
+ }
+ }
+
+ @Override
+ public String toString() {
+ synchronized (mSharedLock) {
+ return "BinderLocationTimeZoneProvider{"
+ + "mProviderName=" + mProviderName
+ + "mCurrentState=" + mCurrentState
+ + "mProxy=" + mProxy
+ + '}';
+ }
+ }
+
+ /**
+ * Passes the supplied simulation / testing event to the current proxy iff the proxy is a
+ * {@link SimulatedLocationTimeZoneProviderProxy}. If not, the event is logged but discarded.
+ */
+ void simulateBinderProviderEvent(SimulatedBinderProviderEvent event) {
+ if (!(mProxy instanceof SimulatedLocationTimeZoneProviderProxy)) {
+ Slog.w(TAG, mProxy + " is not a " + SimulatedLocationTimeZoneProviderProxy.class
+ + ", event=" + event);
+ return;
+ }
+ ((SimulatedLocationTimeZoneProviderProxy) mProxy).simulate(event);
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/ControllerCallbackImpl.java b/services/core/java/com/android/server/location/timezone/ControllerCallbackImpl.java
new file mode 100644
index 0000000..cd9aa2f
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/ControllerCallbackImpl.java
@@ -0,0 +1,43 @@
+/*
+ * 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.location.timezone;
+
+import android.annotation.NonNull;
+
+import com.android.server.LocalServices;
+import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+import com.android.server.timezonedetector.TimeZoneDetectorInternal;
+
+/**
+ * The real implementation of {@link LocationTimeZoneProviderController.Callback} used by
+ * {@link ControllerImpl} to interact with other server components.
+ */
+class ControllerCallbackImpl extends LocationTimeZoneProviderController.Callback {
+
+ ControllerCallbackImpl(@NonNull ThreadingDomain threadingDomain) {
+ super(threadingDomain);
+ }
+
+ @Override
+ void suggest(@NonNull GeolocationTimeZoneSuggestion suggestion) {
+ mThreadingDomain.assertCurrentThread();
+
+ TimeZoneDetectorInternal timeZoneDetector =
+ LocalServices.getService(TimeZoneDetectorInternal.class);
+ timeZoneDetector.suggestGeolocationTimeZone(suggestion);
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java
new file mode 100644
index 0000000..2e2481c
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java
@@ -0,0 +1,51 @@
+/*
+ * 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.location.timezone;
+
+import android.annotation.NonNull;
+
+import com.android.server.LocalServices;
+import com.android.server.timezonedetector.ConfigurationInternal;
+import com.android.server.timezonedetector.TimeZoneDetectorInternal;
+
+import java.util.Objects;
+
+/**
+ * The real implementation of {@link LocationTimeZoneProviderController.Environment} used by
+ * {@link ControllerImpl} to interact with other server components.
+ */
+class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment {
+
+ @NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal;
+ @NonNull private final LocationTimeZoneProviderController mController;
+
+ ControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain,
+ @NonNull LocationTimeZoneProviderController controller) {
+ super(threadingDomain);
+ mController = Objects.requireNonNull(controller);
+ mTimeZoneDetectorInternal = LocalServices.getService(TimeZoneDetectorInternal.class);
+
+ // Listen for configuration changes.
+ mTimeZoneDetectorInternal.addConfigurationListener(
+ () -> mThreadingDomain.post(mController::onConfigChanged));
+ }
+
+ @Override
+ ConfigurationInternal getCurrentUserConfigurationInternal() {
+ return mTimeZoneDetectorInternal.getCurrentUserConfigurationInternal();
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/ControllerImpl.java b/services/core/java/com/android/server/location/timezone/ControllerImpl.java
new file mode 100644
index 0000000..e31cfc4
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/ControllerImpl.java
@@ -0,0 +1,420 @@
+/*
+ * 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.location.timezone;
+
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE;
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS;
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN;
+
+import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
+import static com.android.server.location.timezone.LocationTimeZoneManagerService.warnLog;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.location.timezone.LocationTimeZoneEvent;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.location.timezone.ThreadingDomain.SingleRunnableQueue;
+import com.android.server.timezonedetector.ConfigurationInternal;
+import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+
+import java.time.Duration;
+import java.util.Objects;
+
+/**
+ * A real implementation of {@link LocationTimeZoneProviderController} that supports a single
+ * {@link LocationTimeZoneProvider}.
+ *
+ * TODO(b/152744911): This implementation currently only supports a single ("primary") provider.
+ * Support for a secondary provider will be added in a later commit.
+ */
+class ControllerImpl extends LocationTimeZoneProviderController {
+
+ @VisibleForTesting
+ static final Duration UNCERTAINTY_DELAY = Duration.ofMinutes(5);
+
+ @NonNull private final LocationTimeZoneProvider mProvider;
+ @NonNull private final SingleRunnableQueue mDelayedSuggestionQueue;
+
+ @GuardedBy("mSharedLock")
+ // Non-null after initialize()
+ private ConfigurationInternal mCurrentUserConfiguration;
+
+ @GuardedBy("mSharedLock")
+ // Non-null after initialize()
+ private Environment mEnvironment;
+
+ @GuardedBy("mSharedLock")
+ // Non-null after initialize()
+ private Callback mCallback;
+
+ /**
+ * Contains any currently pending suggestion on {@link #mDelayedSuggestionQueue}, if there is
+ * one.
+ */
+ @GuardedBy("mSharedLock")
+ @Nullable
+ private GeolocationTimeZoneSuggestion mPendingSuggestion;
+
+ /** Contains the last suggestion actually made, if there is one. */
+ @GuardedBy("mSharedLock")
+ @Nullable
+ private GeolocationTimeZoneSuggestion mLastSuggestion;
+
+ ControllerImpl(@NonNull ThreadingDomain threadingDomain,
+ @NonNull LocationTimeZoneProvider provider) {
+ super(threadingDomain);
+ mDelayedSuggestionQueue = threadingDomain.createSingleRunnableQueue();
+ mProvider = Objects.requireNonNull(provider);
+ }
+
+ @Override
+ void initialize(@NonNull Environment environment, @NonNull Callback callback) {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ debugLog("initialize()");
+ mEnvironment = Objects.requireNonNull(environment);
+ mCallback = Objects.requireNonNull(callback);
+ mCurrentUserConfiguration = environment.getCurrentUserConfigurationInternal();
+
+ mProvider.initialize(ControllerImpl.this::onProviderStateChange);
+ enableOrDisableProvider(mCurrentUserConfiguration);
+ }
+ }
+
+ @Override
+ void onConfigChanged() {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ debugLog("onEnvironmentConfigChanged()");
+
+ ConfigurationInternal oldConfig = mCurrentUserConfiguration;
+ ConfigurationInternal newConfig = mEnvironment.getCurrentUserConfigurationInternal();
+ mCurrentUserConfiguration = newConfig;
+
+ if (!newConfig.equals(oldConfig)) {
+ if (newConfig.getUserId() != oldConfig.getUserId()) {
+ // If the user changed, disable the provider if needed. It may be re-enabled for
+ // the new user below if their settings allow.
+ debugLog("User changed. old=" + oldConfig.getUserId()
+ + ", new=" + newConfig.getUserId());
+ debugLog("Disabling LocationTimeZoneProviders as needed");
+ if (mProvider.getCurrentState().stateEnum == PROVIDER_STATE_ENABLED) {
+ mProvider.disable();
+ }
+ }
+
+ enableOrDisableProvider(newConfig);
+ }
+ }
+ }
+
+ @GuardedBy("mSharedLock")
+ private void enableOrDisableProvider(@NonNull ConfigurationInternal configuration) {
+ ProviderState providerState = mProvider.getCurrentState();
+ boolean geoDetectionEnabled = configuration.getGeoDetectionEnabledBehavior();
+ boolean providerWasEnabled = providerState.stateEnum == PROVIDER_STATE_ENABLED;
+ if (geoDetectionEnabled) {
+ switch (providerState.stateEnum) {
+ case PROVIDER_STATE_DISABLED: {
+ debugLog("Enabling " + mProvider);
+ mProvider.enable(configuration);
+ break;
+ }
+ case PROVIDER_STATE_ENABLED: {
+ debugLog("No need to enable " + mProvider + ": already enabled");
+ break;
+ }
+ case PROVIDER_STATE_PERM_FAILED: {
+ debugLog("Unable to enable " + mProvider + ": it is perm failed");
+ break;
+ }
+ default:
+ warnLog("Unknown provider state: " + mProvider);
+ break;
+ }
+ } else {
+ switch (providerState.stateEnum) {
+ case PROVIDER_STATE_DISABLED: {
+ debugLog("No need to disable " + mProvider + ": already enabled");
+ break;
+ }
+ case PROVIDER_STATE_ENABLED: {
+ debugLog("Disabling " + mProvider);
+ mProvider.disable();
+ break;
+ }
+ case PROVIDER_STATE_PERM_FAILED: {
+ debugLog("Unable to disable " + mProvider + ": it is perm failed");
+ break;
+ }
+ default: {
+ warnLog("Unknown provider state: " + mProvider);
+ break;
+ }
+ }
+ }
+
+ boolean isProviderEnabled =
+ mProvider.getCurrentState().stateEnum == PROVIDER_STATE_ENABLED;
+
+ if (isProviderEnabled) {
+ if (!providerWasEnabled) {
+ // When a provider has first been enabled, we allow it some time for it to
+ // initialize.
+ // This sets up an empty suggestion to trigger if no explicit "certain" or
+ // "uncertain" suggestion preempts it within UNCERTAINTY_DELAY. If, for some reason,
+ // the provider does provide any events then this scheduled suggestion will ensure
+ // the controller makes at least an uncertain suggestion.
+ suggestDelayed(createEmptySuggestion(
+ "No event received in delay=" + UNCERTAINTY_DELAY), UNCERTAINTY_DELAY);
+ }
+ } else {
+ // Clear any queued suggestions.
+ clearDelayedSuggestion();
+
+ // If the provider is now not enabled, and a previous "certain" suggestion has been
+ // made, then a new "uncertain" suggestion must be made to indicate the provider no
+ // longer has an opinion and will not be sending updates.
+ if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) {
+ suggestImmediate(createEmptySuggestion(""));
+ }
+ }
+ }
+
+ void onProviderStateChange(@NonNull ProviderState providerState) {
+ mThreadingDomain.assertCurrentThread();
+ assertProviderKnown(providerState.provider);
+
+ synchronized (mSharedLock) {
+ switch (providerState.stateEnum) {
+ case PROVIDER_STATE_DISABLED: {
+ // This should never happen: entering disabled does not trigger an event.
+ warnLog("onProviderStateChange: Unexpected state change for disabled provider,"
+ + " providerState=" + providerState);
+ break;
+ }
+ case PROVIDER_STATE_ENABLED: {
+ // Entering enabled does not trigger an event, so this only happens if an event
+ // is received while the provider is enabled.
+ debugLog("onProviderStateChange: Received notification of an event while"
+ + " enabled, providerState=" + providerState);
+ providerEnabledProcessEvent(providerState);
+ break;
+ }
+ case PROVIDER_STATE_PERM_FAILED: {
+ debugLog("Received notification of permanent failure for"
+ + " provider=" + providerState);
+ GeolocationTimeZoneSuggestion suggestion = createEmptySuggestion(
+ "provider=" + providerState.provider
+ + " permanently failed: " + providerState);
+ suggestImmediate(suggestion);
+ break;
+ }
+ default: {
+ warnLog("onProviderStateChange: Unexpected providerState=" + providerState);
+ }
+ }
+ }
+ }
+
+ private void assertProviderKnown(LocationTimeZoneProvider provider) {
+ if (provider != mProvider) {
+ throw new IllegalArgumentException("Unknown provider: " + provider);
+ }
+ }
+
+ /**
+ * Called when a provider has changed state but just moved from a PROVIDER_STATE_ENABLED state
+ * to another PROVIDER_STATE_ENABLED state, usually as a result of a new {@link
+ * LocationTimeZoneEvent} being received. There are some cases where event can be null.
+ */
+ private void providerEnabledProcessEvent(@NonNull ProviderState providerState) {
+ LocationTimeZoneEvent event = providerState.event;
+ if (event == null) {
+ // Implicit uncertainty, i.e. where the provider is enabled, but a problem has been
+ // detected without having received an event. For example, if the process has detected
+ // the loss of a binder-based provider. This is treated like explicit uncertainty, i.e.
+ // where the provider has explicitly told this process it is uncertain.
+ scheduleUncertainSuggestionIfNeeded(null);
+ return;
+ }
+
+ // Consistency check for user. This may be possible as there are various races around
+ // current user switches.
+ if (!Objects.equals(event.getUserHandle(), mCurrentUserConfiguration.getUserHandle())) {
+ warnLog("Using event=" + event + " from a different user="
+ + mCurrentUserConfiguration);
+ }
+
+ if (!mCurrentUserConfiguration.getGeoDetectionEnabledBehavior()) {
+ // This should not happen: the provider should not be in an enabled state if the user
+ // does not have geodetection enabled.
+ warnLog("Provider=" + providerState + " is enabled, but currentUserConfiguration="
+ + mCurrentUserConfiguration + " suggests it shouldn't be.");
+ }
+
+ switch (event.getEventType()) {
+ case EVENT_TYPE_PERMANENT_FAILURE: {
+ // This shouldn't happen. Providers cannot be enabled and have this event.
+ warnLog("Provider=" + providerState
+ + " is enabled, but event suggests it shouldn't be");
+ break;
+ }
+ case EVENT_TYPE_UNCERTAIN: {
+ scheduleUncertainSuggestionIfNeeded(event);
+ break;
+ }
+ case EVENT_TYPE_SUCCESS: {
+ GeolocationTimeZoneSuggestion suggestion =
+ new GeolocationTimeZoneSuggestion(event.getTimeZoneIds());
+ suggestion.addDebugInfo("Event received provider=" + mProvider.getName()
+ + ", event=" + event);
+ // Rely on the receiver to dedupe events. It is better to over-communicate.
+ suggestImmediate(suggestion);
+ break;
+ }
+ default: {
+ warnLog("Unknown eventType=" + event.getEventType());
+ break;
+ }
+ }
+ }
+
+ /**
+ * Indicates a provider has become uncertain with the event (if any) received that indicates
+ * that.
+ *
+ * <p>Providers are expected to report their uncertainty as soon as they become uncertain, as
+ * this enables the most flexibility for the controller to enable other providers when there are
+ * multiple ones. The controller is therefore responsible for deciding when to make a
+ * "uncertain" suggestion.
+ *
+ * <p>This method schedules an "uncertain" suggestion (if one isn't already scheduled) to be
+ * made later if nothing else preempts it. It can be preempted if the provider becomes certain
+ * (or does anything else that calls {@link #suggestImmediate(GeolocationTimeZoneSuggestion)})
+ * within UNCERTAINTY_DELAY. Preemption causes the scheduled "uncertain" event to be cancelled.
+ * If the provider repeatedly sends uncertainty events within UNCERTAINTY_DELAY, those events
+ * are effectively ignored (i.e. the timer is not reset each time).
+ */
+ private void scheduleUncertainSuggestionIfNeeded(@Nullable LocationTimeZoneEvent event) {
+ if (mPendingSuggestion == null || mPendingSuggestion.getZoneIds() != null) {
+ GeolocationTimeZoneSuggestion suggestion = createEmptySuggestion(
+ "provider=" + mProvider + " became uncertain, event=" + event);
+ suggestDelayed(suggestion, UNCERTAINTY_DELAY);
+ }
+ }
+
+ @Override
+ public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
+ synchronized (mSharedLock) {
+ ipw.println("LocationTimeZoneProviderController:");
+
+ ipw.increaseIndent(); // level 1
+ ipw.println("mCurrentUserConfiguration=" + mCurrentUserConfiguration);
+ ipw.println("mPendingSuggestion=" + mPendingSuggestion);
+ ipw.println("mLastSuggestion=" + mLastSuggestion);
+
+ ipw.println("Provider:");
+ ipw.increaseIndent(); // level 2
+ mProvider.dump(ipw, args);
+ ipw.decreaseIndent(); // level 2
+
+ ipw.decreaseIndent(); // level 1
+ }
+ }
+
+ /** Sends an immediate suggestion, cancelling any pending suggestion. */
+ @GuardedBy("mSharedLock")
+ private void suggestImmediate(@NonNull GeolocationTimeZoneSuggestion suggestion) {
+ debugLog("suggestImmediate: Executing suggestion=" + suggestion);
+ mDelayedSuggestionQueue.runSynchronously(() -> mCallback.suggest(suggestion));
+ mPendingSuggestion = null;
+ mLastSuggestion = suggestion;
+ }
+
+ /** Clears any pending suggestion. */
+ @GuardedBy("mSharedLock")
+ private void clearDelayedSuggestion() {
+ mDelayedSuggestionQueue.cancel();
+ mPendingSuggestion = null;
+ }
+
+
+ /**
+ * Schedules a delayed suggestion. There can only be one delayed suggestion at a time.
+ * If there is a pending scheduled suggestion equal to the one passed, it will not be replaced.
+ * Replacing a previous delayed suggestion has the effect of cancelling the timeout associated
+ * with that previous suggestion.
+ */
+ @GuardedBy("mSharedLock")
+ private void suggestDelayed(@NonNull GeolocationTimeZoneSuggestion suggestion,
+ @NonNull Duration delay) {
+ Objects.requireNonNull(suggestion);
+ Objects.requireNonNull(delay);
+
+ if (Objects.equals(mPendingSuggestion, suggestion)) {
+ // Do not reset the timer.
+ debugLog("suggestDelayed: Suggestion=" + suggestion + " is equal to existing."
+ + " Not scheduled.");
+ return;
+ }
+
+ debugLog("suggestDelayed: Scheduling suggestion=" + suggestion);
+ mPendingSuggestion = suggestion;
+
+ mDelayedSuggestionQueue.runDelayed(() -> {
+ debugLog("suggestDelayed: Executing suggestion=" + suggestion);
+ mCallback.suggest(suggestion);
+ mPendingSuggestion = null;
+ mLastSuggestion = suggestion;
+ }, delay.toMillis());
+ }
+
+ private static GeolocationTimeZoneSuggestion createEmptySuggestion(String reason) {
+ GeolocationTimeZoneSuggestion suggestion = new GeolocationTimeZoneSuggestion(null);
+ suggestion.addDebugInfo(reason);
+ return suggestion;
+ }
+
+ /**
+ * Asynchronously passes a {@link SimulatedBinderProviderEvent] to the appropriate provider.
+ * If the provider name does not match a known provider, then the event is logged and discarded.
+ */
+ void simulateBinderProviderEvent(SimulatedBinderProviderEvent event) {
+ if (!Objects.equals(mProvider.getName(), event.getProviderName())) {
+ warnLog("Unable to process simulated binder provider event,"
+ + " unknown providerName in event=" + event);
+ return;
+ }
+ if (!(mProvider instanceof BinderLocationTimeZoneProvider)) {
+ warnLog("Unable to process simulated binder provider event,"
+ + " provider is not a " + BinderLocationTimeZoneProvider.class
+ + ", event=" + event);
+ return;
+ }
+ ((BinderLocationTimeZoneProvider) mProvider).simulateBinderProviderEvent(event);
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java b/services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java
new file mode 100644
index 0000000..17e719e
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.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 com.android.server.location.timezone;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+
+import java.util.Objects;
+
+/**
+ * The real implementation of {@link ThreadingDomain} that uses a {@link Handler}.
+ */
+final class HandlerThreadingDomain extends ThreadingDomain {
+
+ @NonNull private final Handler mHandler;
+
+ HandlerThreadingDomain(Handler handler) {
+ mHandler = Objects.requireNonNull(handler);
+ }
+
+ /**
+ * Returns the {@link Handler} associated with this threading domain. The same {@link Handler}
+ * may be associated with multiple threading domains, e.g. multiple threading domains could
+ * choose to use the {@link com.android.server.FgThread} handler.
+ *
+ * <p>If you find yourself making this public because you need a {@link Handler}, then it may
+ * cause problems with testability. Try to avoid using this method and use methods like {@link
+ * #post(Runnable)} instead.
+ */
+ @NonNull
+ Handler getHandler() {
+ return mHandler;
+ }
+
+ @NonNull
+ Thread getThread() {
+ return getHandler().getLooper().getThread();
+ }
+
+ @Override
+ void post(@NonNull Runnable r) {
+ getHandler().post(r);
+ }
+
+ @Override
+ void postDelayed(@NonNull Runnable r, long delayMillis) {
+ getHandler().postDelayed(r, delayMillis);
+ }
+
+ @Override
+ void postDelayed(Runnable r, Object token, long delayMillis) {
+ getHandler().postDelayed(r, token, delayMillis);
+ }
+
+ @Override
+ void removeQueuedRunnables(Object token) {
+ getHandler().removeCallbacksAndMessages(token);
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
new file mode 100644
index 0000000..238f999
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
@@ -0,0 +1,269 @@
+/*
+ * 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.location.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Binder;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.SystemProperties;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.DumpUtils;
+import com.android.server.FgThread;
+import com.android.server.SystemService;
+import com.android.server.timezonedetector.TimeZoneDetectorInternal;
+import com.android.server.timezonedetector.TimeZoneDetectorService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Objects;
+
+/**
+ * A service class that acts as a container for the {@link LocationTimeZoneProviderController},
+ * which determines what {@link com.android.server.timezonedetector.GeolocationTimeZoneSuggestion}
+ * are made to the {@link TimeZoneDetectorInternal}, and the {@link LocationTimeZoneProvider}s that
+ * offer {@link android.location.timezone.LocationTimeZoneEvent}s.
+ *
+ * TODO(b/152744911): This implementation currently only supports a primary provider. Support for a
+ * secondary provider must be added in a later commit.
+ *
+ * <p>Implementation details:
+ *
+ * <p>For simplicity, with the exception of a few outliers like {@link #dump}, all processing in
+ * this service (and package-private helper objects) takes place on a single thread / handler, the
+ * one indicated by {@link ThreadingDomain}. Because methods like {@link #dump} can be invoked on
+ * another thread, the service and its related objects must still be thread-safe.
+ *
+ * <p>For testing / reproduction of bugs, it is possible to put providers into "simulation
+ * mode" where the real binder clients are replaced by {@link
+ * SimulatedLocationTimeZoneProviderProxy}. This means that the real client providers are never
+ * bound (ensuring no real location events will be received) and simulated events / behaviors
+ * can be injected via the command line. To enter simulation mode for a provider, use
+ * "{@code adb shell setprop persist.sys.location_tz_simulation_mode.<provider name> 1}" and reboot.
+ * e.g. "{@code adb shell setprop persist.sys.location_tz_simulation_mode.primary 1}}"
+ * Then use "{@code adb shell cmd location_time_zone_manager help}" for injection. Set the system
+ * properties to "0" and reboot to return to exit simulation mode.
+ */
+public class LocationTimeZoneManagerService extends Binder {
+
+ /**
+ * Controls lifecycle of the {@link LocationTimeZoneManagerService}.
+ */
+ public static class Lifecycle extends SystemService {
+
+ private LocationTimeZoneManagerService mService;
+
+ public Lifecycle(@NonNull Context context) {
+ super(Objects.requireNonNull(context));
+ }
+
+ @Override
+ public void onStart() {
+ if (TimeZoneDetectorService.GEOLOCATION_TIME_ZONE_DETECTION_ENABLED) {
+ Context context = getContext();
+ mService = new LocationTimeZoneManagerService(context);
+
+ // The service currently exposes no LocalService or Binder API, but it extends
+ // Binder and is registered as a binder service so it can receive shell commands.
+ publishBinderService("location_time_zone_manager", mService);
+ } else {
+ Slog.i(TAG, getClass() + " is compile-time disabled");
+ }
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (TimeZoneDetectorService.GEOLOCATION_TIME_ZONE_DETECTION_ENABLED) {
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ // The location service must be functioning after this boot phase.
+ mService.onSystemReady();
+ } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+ // Some providers rely on non-platform code (e.g. gcore), so we wait to
+ // initialize providers until third party code is allowed to run.
+ mService.onSystemThirdPartyAppsCanStart();
+ }
+ }
+ }
+ }
+
+ static final String TAG = "LocationTZDetector";
+
+ static final String PRIMARY_PROVIDER_NAME = "primary";
+
+ private static final String SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX =
+ "persist.sys.location_tz_simulation_mode.";
+
+ private static final String ATTRIBUTION_TAG = "LocationTimeZoneService";
+
+ private static final String PRIMARY_LOCATION_TIME_ZONE_SERVICE_ACTION =
+ "com.android.location.timezone.service.v1.PrimaryLocationTimeZoneProvider";
+
+
+ @NonNull private final Context mContext;
+
+ /**
+ * The {@link ThreadingDomain} used to supply the {@link android.os.Handler} and shared lock
+ * object used by the controller and related components.
+ *
+ * <p>Most operations are executed on the associated handler thread <em>but not all</em>, hence
+ * the requirement for additional synchronization using a shared lock.
+ */
+ @NonNull private final ThreadingDomain mThreadingDomain;
+
+ /** The shared lock from {@link #mThreadingDomain}. */
+ @NonNull private final Object mSharedLock;
+
+ // Lazily initialized. Non-null and effectively final after onSystemThirdPartyAppsCanStart().
+ @GuardedBy("mSharedLock")
+ private ControllerImpl mLocationTimeZoneDetectorController;
+
+ LocationTimeZoneManagerService(Context context) {
+ mContext = context.createAttributionContext(ATTRIBUTION_TAG);
+ mThreadingDomain = new HandlerThreadingDomain(FgThread.getHandler());
+ mSharedLock = mThreadingDomain.getLockObject();
+ }
+
+ void onSystemReady() {
+ // Called on an arbitrary thread during initialization.
+ synchronized (mSharedLock) {
+ // TODO(b/152744911): LocationManagerService watches for packages disappearing. Need to
+ // do anything here?
+
+ // TODO(b/152744911): LocationManagerService watches for foreground app changes. Need to
+ // do anything here?
+ // TODO(b/152744911): LocationManagerService watches screen state. Need to do anything
+ // here?
+ }
+ }
+
+ void onSystemThirdPartyAppsCanStart() {
+ // Called on an arbitrary thread during initialization.
+ synchronized (mSharedLock) {
+ LocationTimeZoneProvider primary = createPrimaryProvider();
+ mLocationTimeZoneDetectorController = new ControllerImpl(mThreadingDomain, primary);
+ ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain);
+ ControllerEnvironmentImpl environment = new ControllerEnvironmentImpl(
+ mThreadingDomain, mLocationTimeZoneDetectorController);
+
+ // Initialize the controller on the mThreadingDomain thread: this ensures that the
+ // ThreadingDomain requirements for the controller / environment methods are honored.
+ mThreadingDomain.post(() ->
+ mLocationTimeZoneDetectorController.initialize(environment, callback));
+ }
+ }
+
+ private LocationTimeZoneProvider createPrimaryProvider() {
+ LocationTimeZoneProviderProxy proxy;
+ if (isInSimulationMode(PRIMARY_PROVIDER_NAME)) {
+ proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
+ } else {
+ // TODO Uncomment this code in a later commit.
+ throw new UnsupportedOperationException("Not implemented");
+ /*
+ proxy = RealLocationTimeZoneProviderProxy.createAndRegister(
+ mContext,
+ mThreadingDomain,
+ PRIMARY_LOCATION_TIME_ZONE_SERVICE_ACTION,
+ com.android.internal.R.bool.config_enablePrimaryLocationTimeZoneOverlay,
+ com.android.internal.R.string.config_primaryLocationTimeZoneProviderPackageName
+ );
+ */
+ }
+ return createLocationTimeZoneProvider(PRIMARY_PROVIDER_NAME, proxy);
+ }
+
+ private boolean isInSimulationMode(String providerName) {
+ return SystemProperties.getBoolean(
+ SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX + providerName, false);
+ }
+
+ private LocationTimeZoneProvider createLocationTimeZoneProvider(
+ @NonNull String providerName, @NonNull LocationTimeZoneProviderProxy proxy) {
+ LocationTimeZoneProvider provider;
+ if (proxy != null) {
+ debugLog("LocationTimeZoneProvider found for providerName=" + providerName);
+ provider = new BinderLocationTimeZoneProvider(mThreadingDomain,
+ providerName, proxy);
+ } else {
+ debugLog("No LocationTimeZoneProvider found for providerName=" + providerName
+ + ": stubbing");
+ provider = new NullLocationTimeZoneProvider(mThreadingDomain, providerName);
+ }
+ return provider;
+ }
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
+ (new LocationTimeZoneManagerShellCommand(this)).exec(
+ this, in, out, err, args, callback, resultReceiver);
+ }
+
+ /**
+ * Asynchronously passes a {@link SimulatedBinderProviderEvent] to the appropriate provider.
+ * The device must be in simulation mode, otherwise an {@link IllegalStateException} will be
+ * thrown.
+ */
+ void simulateBinderProviderEvent(SimulatedBinderProviderEvent event)
+ throws IllegalStateException {
+ if (!isInSimulationMode(event.getProviderName())) {
+ throw new IllegalStateException("Use \"setprop "
+ + SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX + event.getProviderName()
+ + " 1\" and reboot before injecting simulated binder events.");
+ }
+ mThreadingDomain.post(
+ () -> mLocationTimeZoneDetectorController.simulateBinderProviderEvent(event));
+ }
+
+ @Override
+ protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
+ @Nullable String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+ // Called on an arbitrary thread at any time.
+ synchronized (mSharedLock) {
+ ipw.println("LocationTimeZoneManagerService:");
+ ipw.increaseIndent();
+ if (mLocationTimeZoneDetectorController == null) {
+ ipw.println("{Uninitialized}");
+ } else {
+ mLocationTimeZoneDetectorController.dump(ipw, args);
+ }
+ ipw.decreaseIndent();
+ }
+ }
+
+ static void debugLog(String msg) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Slog.d(TAG, msg);
+ }
+ }
+
+ static void warnLog(String msg) {
+ if (Log.isLoggable(TAG, Log.WARN)) {
+ Slog.w(TAG, msg);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java
new file mode 100644
index 0000000..7c3b891
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java
@@ -0,0 +1,80 @@
+/*
+ * 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.location.timezone;
+
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+/** Implements the shell command interface for {@link LocationTimeZoneManagerService}. */
+class LocationTimeZoneManagerShellCommand extends ShellCommand {
+
+ private final LocationTimeZoneManagerService mService;
+
+ LocationTimeZoneManagerShellCommand(LocationTimeZoneManagerService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+
+ switch (cmd) {
+ case "simulate_binder": {
+ return runSimulateBinderEvent();
+ }
+ default: {
+ return handleDefaultCommands(cmd);
+ }
+ }
+ }
+
+ private int runSimulateBinderEvent() {
+ PrintWriter outPrintWriter = getOutPrintWriter();
+
+ SimulatedBinderProviderEvent simulatedProviderBinderEvent;
+ try {
+ simulatedProviderBinderEvent = SimulatedBinderProviderEvent.createFromArgs(this);
+ } catch (IllegalArgumentException e) {
+ outPrintWriter.println("Error: " + e.getMessage());
+ return 1;
+ }
+
+ outPrintWriter.println("Injecting: " + simulatedProviderBinderEvent);
+ try {
+ mService.simulateBinderProviderEvent(simulatedProviderBinderEvent);
+ } catch (IllegalStateException e) {
+ outPrintWriter.println("Error: " + e.getMessage());
+ return 2;
+ }
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("Location Time Zone Manager (location_time_zone_manager) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" simulate_binder");
+ pw.println(" <simulated provider binder event>");
+ pw.println();
+ SimulatedBinderProviderEvent.printCommandLineOpts(pw);
+ pw.println();
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java
new file mode 100644
index 0000000..3743779
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java
@@ -0,0 +1,511 @@
+/*
+ * 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.location.timezone;
+
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE;
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS;
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN;
+
+import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.location.timezone.LocationTimeZoneEvent;
+import android.os.Handler;
+import android.os.SystemClock;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.timezonedetector.ConfigurationInternal;
+import com.android.server.timezonedetector.Dumpable;
+import com.android.server.timezonedetector.ReferenceWithHistory;
+
+import java.util.Objects;
+
+/**
+ * A facade used by the {@link LocationTimeZoneProviderController} to interact with a location time
+ * zone provider. The provider could have a binder implementation with logic running in another
+ * process, or could be a stubbed instance when no real provider is registered.
+ *
+ * <p>The provider is supplied with a {@link ProviderListener} via {@link
+ * #initialize(ProviderListener)}. This enables it to communicates asynchronous detection / error
+ * events back to the {@link LocationTimeZoneProviderController} via the {@link
+ * ProviderListener#onProviderStateChange} method. This call must be made on the
+ * {@link Handler} thread from the {@link ThreadingDomain} passed to the constructor.
+ *
+ * <p>All incoming calls from the controller except for {@link
+ * LocationTimeZoneProvider#dump(android.util.IndentingPrintWriter, String[])} will be made on the
+ * {@link Handler} thread of the {@link ThreadingDomain} passed to the constructor.
+ */
+abstract class LocationTimeZoneProvider implements Dumpable {
+
+ /**
+ * Listener interface used by the {@link LocationTimeZoneProviderController} to register an
+ * interest in provider events.
+ */
+ interface ProviderListener {
+ /**
+ * Indicated that a provider changed states. The {@code providerState} indicates which one
+ */
+ void onProviderStateChange(@NonNull ProviderState providerState);
+ }
+
+ /**
+ * Information about the provider's current state.
+ */
+ static class ProviderState {
+
+ @IntDef({ PROVIDER_STATE_UNKNOWN, PROVIDER_STATE_ENABLED, PROVIDER_STATE_DISABLED,
+ PROVIDER_STATE_PERM_FAILED })
+ @interface ProviderStateEnum {}
+
+ /**
+ * Uninitialized value. Must not be used afte {@link LocationTimeZoneProvider#initialize}.
+ */
+ static final int PROVIDER_STATE_UNKNOWN = 0;
+
+ /**
+ * The provider is currently enabled.
+ */
+ static final int PROVIDER_STATE_ENABLED = 1;
+
+ /**
+ * The provider is currently disabled.
+ * This is the state after {@link #initialize} is called.
+ */
+ static final int PROVIDER_STATE_DISABLED = 2;
+
+ /**
+ * The provider has failed and cannot be re-enabled.
+ *
+ * Providers may enter this state after a provider is enabled.
+ */
+ static final int PROVIDER_STATE_PERM_FAILED = 3;
+
+ /** The {@link LocationTimeZoneProvider} the state is for. */
+ public final @NonNull LocationTimeZoneProvider provider;
+
+ /** The state enum value of the current state. */
+ public final @ProviderStateEnum int stateEnum;
+
+ /**
+ * The last {@link LocationTimeZoneEvent} received. Only populated when {@link #stateEnum}
+ * is {@link #PROVIDER_STATE_ENABLED}, but it can be {@code null} then too if no event has
+ * yet been received.
+ */
+ @Nullable public final LocationTimeZoneEvent event;
+
+ /**
+ * The user configuration associated with the current state. Only and always present when
+ * {@link #stateEnum} is {@link #PROVIDER_STATE_ENABLED}.
+ */
+ @Nullable public final ConfigurationInternal currentUserConfiguration;
+
+ /**
+ * The time according to the elapsed realtime clock when the provider entered the current
+ * state. Included for debugging, not used for equality.
+ */
+ private final long mStateEntryTimeMillis;
+
+ /**
+ * Debug information providing context for the transition to this state. Included for
+ * debugging, not used for equality.
+ */
+ @Nullable private final String mDebugInfo;
+
+
+ private ProviderState(@NonNull LocationTimeZoneProvider provider,
+ @ProviderStateEnum int stateEnum, @Nullable LocationTimeZoneEvent event,
+ @Nullable ConfigurationInternal currentUserConfiguration,
+ @Nullable String debugInfo) {
+ this.provider = Objects.requireNonNull(provider);
+ this.stateEnum = stateEnum;
+ this.event = event;
+ this.currentUserConfiguration = currentUserConfiguration;
+ this.mStateEntryTimeMillis = SystemClock.elapsedRealtime();
+ this.mDebugInfo = debugInfo;
+ }
+
+ /** Creates the bootstrap state, uses {@link #PROVIDER_STATE_UNKNOWN}. */
+ static ProviderState createStartingState(
+ @NonNull LocationTimeZoneProvider provider) {
+ return new ProviderState(
+ provider, PROVIDER_STATE_UNKNOWN, null, null, "Initial state");
+ }
+
+ /**
+ * Create a new state from this state. Validates that the state transition is valid
+ * and that the required parameters for the new state are present / absent.
+ */
+ ProviderState newState(@ProviderStateEnum int newStateEnum,
+ @Nullable LocationTimeZoneEvent event,
+ @Nullable ConfigurationInternal currentUserConfig,
+ @Nullable String debugInfo) {
+
+ // Check valid "from" transitions.
+ switch (this.stateEnum) {
+ case PROVIDER_STATE_UNKNOWN: {
+ if (newStateEnum != PROVIDER_STATE_DISABLED) {
+ throw new IllegalArgumentException(
+ "Must transition from " + prettyPrintStateEnum(
+ PROVIDER_STATE_UNKNOWN)
+ + " to " + prettyPrintStateEnum(PROVIDER_STATE_DISABLED));
+ }
+ break;
+ }
+ case PROVIDER_STATE_DISABLED:
+ case PROVIDER_STATE_ENABLED: {
+ // These can go to each other or PROVIDER_STATE_PERM_FAILED.
+ break;
+ }
+ case PROVIDER_STATE_PERM_FAILED: {
+ throw new IllegalArgumentException("Illegal transition out of "
+ + prettyPrintStateEnum(PROVIDER_STATE_UNKNOWN));
+ }
+ default: {
+ throw new IllegalArgumentException("Invalid this.stateEnum=" + this.stateEnum);
+ }
+ }
+
+ // Validate "to" transitions / arguments.
+ switch (newStateEnum) {
+ case PROVIDER_STATE_UNKNOWN: {
+ throw new IllegalArgumentException("Cannot transition to "
+ + prettyPrintStateEnum(PROVIDER_STATE_UNKNOWN));
+ }
+ case PROVIDER_STATE_DISABLED: {
+ if (event != null || currentUserConfig != null) {
+ throw new IllegalArgumentException(
+ "Disabled state: event and currentUserConfig must be null"
+ + ", event=" + event
+ + ", currentUserConfig=" + currentUserConfig);
+ }
+ break;
+ }
+ case PROVIDER_STATE_ENABLED: {
+ if (currentUserConfig == null) {
+ throw new IllegalArgumentException(
+ "Enabled state: currentUserConfig must not be null");
+ }
+ break;
+ }
+ case PROVIDER_STATE_PERM_FAILED: {
+ if (event != null || currentUserConfig != null) {
+ throw new IllegalArgumentException(
+ "Perf failed state: event and currentUserConfig must be null"
+ + ", event=" + event
+ + ", currentUserConfig=" + currentUserConfig);
+ }
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("Unknown newStateEnum=" + newStateEnum);
+ }
+ }
+ return new ProviderState(provider, newStateEnum, event, currentUserConfig, debugInfo);
+ }
+
+ @Override
+ public String toString() {
+ return "State{"
+ + "stateEnum=" + prettyPrintStateEnum(stateEnum)
+ + ", event=" + event
+ + ", currentUserConfiguration=" + currentUserConfiguration
+ + ", mStateEntryTimeMillis=" + mStateEntryTimeMillis
+ + ", mDebugInfo=" + mDebugInfo
+ + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ProviderState state = (ProviderState) o;
+ return stateEnum == state.stateEnum
+ && Objects.equals(event, state.event)
+ && Objects.equals(currentUserConfiguration, state.currentUserConfiguration);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(stateEnum, event, currentUserConfiguration);
+ }
+
+ private static String prettyPrintStateEnum(@ProviderStateEnum int state) {
+ switch (state) {
+ case PROVIDER_STATE_DISABLED:
+ return "Disabled (" + PROVIDER_STATE_DISABLED + ")";
+ case PROVIDER_STATE_ENABLED:
+ return "Enabled (" + PROVIDER_STATE_ENABLED + ")";
+ case PROVIDER_STATE_PERM_FAILED:
+ return "Perm failure (" + PROVIDER_STATE_PERM_FAILED + ")";
+ case PROVIDER_STATE_UNKNOWN:
+ default:
+ return "Unknown (" + state + ")";
+ }
+ }
+ }
+
+ @NonNull final ThreadingDomain mThreadingDomain;
+ @NonNull final Object mSharedLock;
+ @NonNull final String mProviderName;
+
+ /**
+ * The current state (with history for debugging).
+ */
+ @GuardedBy("mSharedLock")
+ final ReferenceWithHistory<ProviderState> mCurrentState =
+ new ReferenceWithHistory<>(10);
+
+ // Non-null and effectively final after initialize() is called.
+ ProviderListener mProviderListener;
+
+ /** Creates the instance. */
+ LocationTimeZoneProvider(@NonNull ThreadingDomain threadingDomain,
+ @NonNull String providerName) {
+ mThreadingDomain = Objects.requireNonNull(threadingDomain);
+ mSharedLock = threadingDomain.getLockObject();
+ mProviderName = Objects.requireNonNull(providerName);
+ }
+
+ /**
+ * Called before the provider is first used.
+ */
+ final void initialize(@NonNull ProviderListener providerListener) {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ if (mProviderListener != null) {
+ throw new IllegalStateException("initialize already called");
+ }
+ mProviderListener = Objects.requireNonNull(providerListener);
+ ProviderState currentState = ProviderState.createStartingState(this);
+ ProviderState newState = currentState.newState(
+ PROVIDER_STATE_DISABLED, null, null, "initialize() called");
+ setCurrentState(newState, false);
+
+ onInitialize();
+ }
+ }
+
+ /**
+ * Implemented by subclasses to do work during {@link #initialize}.
+ */
+ abstract void onInitialize();
+
+ /**
+ * Set the current state, for use by this class and subclasses only. If {@code #notifyChanges}
+ * is {@code true} and {@code newState} is not equal to the old state, then {@link
+ * ProviderListener#onProviderStateChange(ProviderState)} must be called on
+ * {@link #mProviderListener}.
+ */
+ final void setCurrentState(@NonNull ProviderState newState, boolean notifyChanges) {
+ mThreadingDomain.assertCurrentThread();
+ synchronized (mSharedLock) {
+ ProviderState oldState = mCurrentState.get();
+ mCurrentState.set(newState);
+ onSetCurrentState(newState);
+ if (notifyChanges) {
+ if (!Objects.equals(newState, oldState)) {
+ mProviderListener.onProviderStateChange(newState);
+ }
+ }
+ }
+ }
+
+ /**
+ * Overridden by subclasses to do work during {@link #setCurrentState}.
+ */
+ @GuardedBy("mSharedLock")
+ void onSetCurrentState(ProviderState newState) {
+ // Default no-op.
+ }
+
+ /**
+ * Returns the current state of the provider. This method must be called using the handler
+ * thread from the {@link ThreadingDomain}.
+ */
+ @NonNull
+ final ProviderState getCurrentState() {
+ mThreadingDomain.assertCurrentThread();
+ synchronized (mSharedLock) {
+ return mCurrentState.get();
+ }
+ }
+
+ /**
+ * Returns the name of the provider. This method must be called using the handler thread from
+ * the {@link ThreadingDomain}.
+ */
+ final String getName() {
+ mThreadingDomain.assertCurrentThread();
+ return mProviderName;
+ }
+
+ /**
+ * Enables the provider. It is an error to call this method except when the {@link
+ * #getCurrentState()} is at {@link ProviderState#PROVIDER_STATE_DISABLED}. This method must be
+ * called using the handler thread from the {@link ThreadingDomain}.
+ */
+ final void enable(@NonNull ConfigurationInternal currentUserConfiguration) {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ assertCurrentState(PROVIDER_STATE_DISABLED);
+
+ ProviderState currentState = getCurrentState();
+ ProviderState newState = currentState.newState(
+ PROVIDER_STATE_ENABLED, null, currentUserConfiguration, "enable() called");
+ setCurrentState(newState, false);
+ onEnable();
+ }
+ }
+
+ /**
+ * Implemented by subclasses to do work during {@link #enable}.
+ */
+ abstract void onEnable();
+
+ /**
+ * Disables the provider. It is an error* to call this method except when the {@link
+ * #getCurrentState()} is at {@link ProviderState#PROVIDER_STATE_ENABLED}. This method must be
+ * called using the handler thread from the {@link ThreadingDomain}.
+ */
+ final void disable() {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ assertCurrentState(PROVIDER_STATE_ENABLED);
+
+ ProviderState currentState = getCurrentState();
+ ProviderState newState =
+ currentState.newState(PROVIDER_STATE_DISABLED, null, null, "disable() called");
+ setCurrentState(newState, false);
+
+ onDisable();
+ }
+ }
+
+ /**
+ * Implemented by subclasses to do work during {@link #disable}.
+ */
+ abstract void onDisable();
+
+ /** For subclasses to invoke when a {@link LocationTimeZoneEvent} has been received. */
+ final void handleLocationTimeZoneEvent(
+ @NonNull LocationTimeZoneEvent locationTimeZoneEvent) {
+ mThreadingDomain.assertCurrentThread();
+ Objects.requireNonNull(locationTimeZoneEvent);
+
+ synchronized (mSharedLock) {
+ debugLog("handleLocationTimeZoneEvent: mProviderName=" + mProviderName
+ + ", locationTimeZoneEvent=" + locationTimeZoneEvent);
+
+ ProviderState currentState = getCurrentState();
+ int eventType = locationTimeZoneEvent.getEventType();
+ switch (currentState.stateEnum) {
+ case PROVIDER_STATE_PERM_FAILED: {
+ // After entering perm failed, there is nothing to do. The remote peer is
+ // supposed to stop sending events after it has reported perm failure.
+ logWarn("handleLocationTimeZoneEvent: Event=" + locationTimeZoneEvent
+ + " received for provider=" + this + " when in failed state");
+ return;
+ }
+ case PROVIDER_STATE_DISABLED: {
+ switch (eventType) {
+ case EVENT_TYPE_PERMANENT_FAILURE: {
+ String msg = "handleLocationTimeZoneEvent:"
+ + " Failure event=" + locationTimeZoneEvent
+ + " received for disabled provider=" + this
+ + ", entering permanently failed state";
+ logWarn(msg);
+ ProviderState newState = currentState.newState(
+ PROVIDER_STATE_PERM_FAILED, null, null, msg);
+ setCurrentState(newState, true);
+ return;
+ }
+ case EVENT_TYPE_SUCCESS:
+ case EVENT_TYPE_UNCERTAIN: {
+ // Any geolocation-related events received for a disabled provider are
+ // ignored: they should not happen.
+ logWarn("handleLocationTimeZoneEvent:"
+ + " event=" + locationTimeZoneEvent
+ + " received for disabled provider=" + this
+ + ", ignoring");
+
+ return;
+ }
+ default: {
+ throw new IllegalStateException(
+ "Unknown eventType=" + locationTimeZoneEvent);
+ }
+ }
+ }
+ case PROVIDER_STATE_ENABLED: {
+ switch (eventType) {
+ case EVENT_TYPE_PERMANENT_FAILURE: {
+ String msg = "handleLocationTimeZoneEvent:"
+ + " Failure event=" + locationTimeZoneEvent
+ + " received for provider=" + this
+ + ", entering permanently failed state";
+ logWarn(msg);
+ ProviderState newState = currentState.newState(
+ PROVIDER_STATE_PERM_FAILED, null, null, msg);
+ setCurrentState(newState, true);
+ return;
+ }
+ case EVENT_TYPE_UNCERTAIN:
+ case EVENT_TYPE_SUCCESS: {
+ ProviderState newState = currentState.newState(PROVIDER_STATE_ENABLED,
+ locationTimeZoneEvent, currentState.currentUserConfiguration,
+ "handleLocationTimeZoneEvent() when enabled");
+ setCurrentState(newState, true);
+ return;
+ }
+ default: {
+ throw new IllegalStateException(
+ "Unknown eventType=" + locationTimeZoneEvent);
+ }
+ }
+ }
+ default: {
+ throw new IllegalStateException("Unknown providerType=" + currentState);
+ }
+ }
+ }
+ }
+
+ /**
+ * Implemented by subclasses.
+ */
+ abstract void logWarn(String msg);
+
+ private void assertCurrentState(@ProviderState.ProviderStateEnum int requiredState) {
+ ProviderState currentState = getCurrentState();
+ if (currentState.stateEnum != requiredState) {
+ throw new IllegalStateException(
+ "Required stateEnum=" + requiredState + ", but was " + currentState);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java
new file mode 100644
index 0000000..2f75c43
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java
@@ -0,0 +1,124 @@
+/*
+ * 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.location.timezone;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+
+import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState;
+import com.android.server.timezonedetector.ConfigurationInternal;
+import com.android.server.timezonedetector.Dumpable;
+import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+
+import java.util.Objects;
+
+/**
+ * An base class for the component responsible handling events from {@link
+ * LocationTimeZoneProvider}s and synthesizing time zone ID suggestions for sending to the time zone
+ * detector. This interface primarily exists to extract testable detection logic, i.e. with
+ * a minimal number of threading considerations or dependencies on Android infrastructure.
+ *
+ * <p>The controller interacts with the following components:
+ * <ul>
+ * <li>The surrounding service, which calls {@link #initialize(Environment, Callback)} and
+ * {@link #onConfigChanged()}.</li>
+ * <li>The {@link Environment} through which obtains information it needs.</li>
+ * <li>The {@link Callback} through which it makes time zone suggestions.</li>
+ * <li>Any {@link LocationTimeZoneProvider} instances it owns, which communicate via the
+ * {@link LocationTimeZoneProvider.ProviderListener#onProviderStateChange(ProviderState)}
+ * method.</li>
+ * </ul>
+ *
+ * <p>All incoming calls except for {@link
+ * LocationTimeZoneProviderController#dump(android.util.IndentingPrintWriter, String[])} must be
+ * made on the {@link Handler} thread of the {@link ThreadingDomain} passed to {@link
+ * #LocationTimeZoneProviderController(ThreadingDomain)}.
+ *
+ * <p>Provider / controller integration notes:
+ *
+ * <p>Providers distinguish between "unknown unknowns" ("uncertain") and "known unknowns"
+ * ("certain"), i.e. a provider can be uncertain and not know what the time zone is, which is
+ * different from the certainty that there are no time zone IDs for the current location. A provider
+ * can be certain about there being no time zone IDs for a location for good reason, e.g. for
+ * disputed areas and oceans. Distinguishing uncertainty allows the controller to try other
+ * providers (or give up), where as certainty means it should not.
+ *
+ * <p>A provider can fail permanently. A permanent failure will disable the provider until next
+ * boot.
+ */
+abstract class LocationTimeZoneProviderController implements Dumpable {
+
+ @NonNull protected final ThreadingDomain mThreadingDomain;
+ @NonNull protected final Object mSharedLock;
+
+ LocationTimeZoneProviderController(@NonNull ThreadingDomain threadingDomain) {
+ mThreadingDomain = Objects.requireNonNull(threadingDomain);
+ mSharedLock = threadingDomain.getLockObject();
+ }
+
+ /**
+ * Called to initialize the controller during boot. Called once only.
+ * {@link LocationTimeZoneProvider#initialize} must be called by this method.
+ */
+ abstract void initialize(@NonNull Environment environment, @NonNull Callback callback);
+
+ /**
+ * Called when any settings or other device state that affect location-based time zone detection
+ * have changed. The receiver should call {@link
+ * Environment#getCurrentUserConfigurationInternal()} to get the current user's config. This
+ * call must be made on the {@link ThreadingDomain} handler thread.
+ */
+ abstract void onConfigChanged();
+
+ /**
+ * Used by {@link LocationTimeZoneProviderController} to obtain information from the surrounding
+ * service. It can easily be faked for tests.
+ */
+ abstract static class Environment {
+
+ @NonNull protected final ThreadingDomain mThreadingDomain;
+ @NonNull protected final Object mSharedLock;
+
+ Environment(@NonNull ThreadingDomain threadingDomain) {
+ mThreadingDomain = Objects.requireNonNull(threadingDomain);
+ mSharedLock = threadingDomain.getLockObject();
+ }
+
+ /** Returns the {@link ConfigurationInternal} for the current user of the device. */
+ abstract ConfigurationInternal getCurrentUserConfigurationInternal();
+ }
+
+ /**
+ * Used by {@link LocationTimeZoneProviderController} to interact with the surrounding service.
+ * It can easily be faked for tests.
+ */
+ abstract static class Callback {
+
+ @NonNull protected final ThreadingDomain mThreadingDomain;
+ @NonNull protected final Object mSharedLock;
+
+ Callback(@NonNull ThreadingDomain threadingDomain) {
+ mThreadingDomain = Objects.requireNonNull(threadingDomain);
+ mSharedLock = threadingDomain.getLockObject();
+ }
+
+ /**
+ * Suggests the latest time zone state for the device.
+ */
+ abstract void suggest(@NonNull GeolocationTimeZoneSuggestion suggestion);
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java
new file mode 100644
index 0000000..3d889ae
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java
@@ -0,0 +1,119 @@
+/*
+ * 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.location.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.location.timezone.LocationTimeZoneEvent;
+import android.os.Handler;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;
+import com.android.server.timezonedetector.Dumpable;
+
+import java.util.Objects;
+
+/**
+ * System server-side proxy for ILocationTimeZoneProvider implementations, i.e. this provides the
+ * system server object used to communicate with a remote LocationTimeZoneProvider over Binder,
+ * which could be running in a different process. As LocationTimeZoneProviders are bound / unbound
+ * this proxy will rebind to the "best" available remote process.
+ *
+ * <p>Threading guarantees provided / required by this interface:
+ * <ul>
+ * <li>All public methods defined by this class must be invoked using the {@link Handler} thread
+ * from the {@link ThreadingDomain} passed to the constructor, excluding
+ * {@link #dump(IndentingPrintWriter, String[])}</li>
+ * <li>Non-static public methods that make binder calls to remote processes (e.g.
+ * {@link #setRequest(LocationTimeZoneProviderRequest)}) are executed asynchronously and will
+ * return immediately.</li>
+ * <li>Callbacks received via binder are delivered via {@link Listener} are delivered on the
+ * {@link Handler} thread from the {@link ThreadingDomain} passed to the constructor.
+ * </ul>
+ *
+ * <p>This class exists to enable the introduction of test implementations of {@link
+ * LocationTimeZoneProviderProxy} that can be used when a device is in a test mode to inject test
+ * events / behavior that are otherwise difficult to simulate.
+ */
+abstract class LocationTimeZoneProviderProxy implements Dumpable {
+
+ @NonNull protected final Context mContext;
+ @NonNull protected final ThreadingDomain mThreadingDomain;
+ @NonNull protected final Object mSharedLock;
+
+ // Non-null and effectively final after setListener() is called.
+ @GuardedBy("mSharedLock")
+ @Nullable
+ protected Listener mListener;
+
+ LocationTimeZoneProviderProxy(
+ @NonNull Context context, @NonNull ThreadingDomain threadingDomain) {
+ mContext = Objects.requireNonNull(context);
+ mThreadingDomain = Objects.requireNonNull(threadingDomain);
+ mSharedLock = threadingDomain.getLockObject();
+ }
+
+ /**
+ * Sets the listener. The listener can expect to receive all events after this point.
+ */
+ void setListener(@NonNull Listener listener) {
+ Objects.requireNonNull(listener);
+ synchronized (mSharedLock) {
+ if (mListener != null) {
+ throw new IllegalStateException("listener already set");
+ }
+ this.mListener = listener;
+ }
+ }
+
+ /**
+ * Sets a new request for the provider.
+ */
+ abstract void setRequest(@NonNull LocationTimeZoneProviderRequest request);
+
+ /**
+ * Handles a {@link LocationTimeZoneEvent} from a remote process.
+ */
+ final void handleLocationTimeZoneEvent(
+ @NonNull LocationTimeZoneEvent locationTimeZoneEvent) {
+ // These calls are invoked on a binder thread. Move to the mThreadingDomain thread as
+ // required by the guarantees for this class.
+ mThreadingDomain.post(() -> mListener.onReportLocationTimeZoneEvent(locationTimeZoneEvent));
+ }
+
+ /**
+ * Interface for listening to location time zone providers. See {@link
+ * LocationTimeZoneProviderProxy} for threading guarantees.
+ */
+ interface Listener {
+
+ /**
+ * Called when a provider receives a {@link LocationTimeZoneEvent}.
+ */
+ void onReportLocationTimeZoneEvent(@NonNull LocationTimeZoneEvent locationTimeZoneEvent);
+
+ /**
+ * Called when a provider is (re)bound.
+ */
+ void onProviderBound();
+
+ /** Called when a provider is unbound. */
+ void onProviderUnbound();
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java
new file mode 100644
index 0000000..79e2b97
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java
@@ -0,0 +1,97 @@
+/*
+ * 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.location.timezone;
+
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+
+/**
+ * A {@link LocationTimeZoneProvider} that provides minimal responses needed for the {@link
+ * LocationTimeZoneProviderController} to operate correctly when there is no "real" provider
+ * configured. This can be used during development / testing, or in a production build when the
+ * platform supports more providers than are needed for an Android deployment.
+ *
+ * <p>For example, if the {@link LocationTimeZoneProviderController} supports a primary
+ * and a secondary {@link LocationTimeZoneProvider}, but only a primary is configured, the secondary
+ * config will be left null and the {@link LocationTimeZoneProvider} implementation will be
+ * defaulted to a {@link NullLocationTimeZoneProvider}. The {@link NullLocationTimeZoneProvider}
+ * enters a {@link ProviderState#PROVIDER_STATE_PERM_FAILED} state immediately after being enabled
+ * for the first time and sends the appropriate event, which ensures the {@link
+ * LocationTimeZoneProviderController} won't expect any further {@link
+ * android.location.timezone.LocationTimeZoneEvent}s to come from it, and won't attempt to use it
+ * again.
+ */
+class NullLocationTimeZoneProvider extends LocationTimeZoneProvider {
+
+ private static final String TAG = "NullLocationTimeZoneProvider";
+
+ /** Creates the instance. */
+ NullLocationTimeZoneProvider(@NonNull ThreadingDomain threadingDomain,
+ @NonNull String providerName) {
+ super(threadingDomain, providerName);
+ }
+
+ @Override
+ void onInitialize() {
+ // No-op
+ }
+
+ @Override
+ void onEnable() {
+ // Report a failure (asynchronously using the mThreadingDomain thread to avoid recursion).
+ mThreadingDomain.post(()-> {
+ // Enter the perm-failed state.
+ ProviderState currentState = mCurrentState.get();
+ ProviderState failedState = currentState.newState(
+ PROVIDER_STATE_PERM_FAILED, null, null, "Stubbed provider");
+ setCurrentState(failedState, true);
+ });
+ }
+
+ @Override
+ void onDisable() {
+ // Ignored - NullLocationTimeZoneProvider is always permanently failed.
+ }
+
+ @Override
+ void logWarn(String msg) {
+ Slog.w(TAG, msg);
+ }
+
+ @Override
+ public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
+ synchronized (mSharedLock) {
+ ipw.println("{Stubbed LocationTimeZoneProvider}");
+ ipw.println("mProviderName=" + mProviderName);
+ ipw.println("mCurrentState=" + mCurrentState);
+ }
+ }
+
+ @Override
+ public String toString() {
+ synchronized (mSharedLock) {
+ return "NullLocationTimeZoneProvider{"
+ + "mProviderName='" + mProviderName + '\''
+ + "mCurrentState='" + mCurrentState + '\''
+ + '}';
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java b/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java
new file mode 100644
index 0000000..ef2e349
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java
@@ -0,0 +1,162 @@
+/*
+ * 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.location.timezone;
+
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE;
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS;
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN;
+
+import static com.android.server.location.timezone.LocationTimeZoneManagerService.PRIMARY_PROVIDER_NAME;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.location.timezone.LocationTimeZoneEvent;
+import android.os.ShellCommand;
+import android.os.SystemClock;
+import android.os.UserHandle;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * An event used for simulating real binder proxy behavior using a {@link
+ * SimulatedLocationTimeZoneProviderProxy}.
+ */
+final class SimulatedBinderProviderEvent {
+
+ private static final List<String> VALID_PROVIDER_NAMES = Arrays.asList(PRIMARY_PROVIDER_NAME);
+
+ static final int INJECTED_EVENT_TYPE_ON_BIND = 1;
+ static final int INJECTED_EVENT_TYPE_ON_UNBIND = 2;
+ static final int INJECTED_EVENT_TYPE_LOCATION_TIME_ZONE_EVENT = 3;
+
+
+ @NonNull private final String mProviderName;
+ private final int mEventType;
+ @Nullable private final LocationTimeZoneEvent mLocationTimeZoneEvent;
+
+ private SimulatedBinderProviderEvent(@NonNull String providerName, int eventType,
+ @Nullable LocationTimeZoneEvent locationTimeZoneEvent) {
+ this.mProviderName = Objects.requireNonNull(providerName);
+ this.mEventType = eventType;
+ this.mLocationTimeZoneEvent = locationTimeZoneEvent;
+ }
+
+ @NonNull
+ String getProviderName() {
+ return mProviderName;
+ }
+
+ @Nullable
+ LocationTimeZoneEvent getLocationTimeZoneEvent() {
+ return mLocationTimeZoneEvent;
+ }
+
+ int getEventType() {
+ return mEventType;
+ }
+
+ /** Prints the command line options that {@link #createFromArgs(ShellCommand)} understands. */
+ static void printCommandLineOpts(PrintWriter pw) {
+ pw.println("Simulated provider binder event:");
+ pw.println();
+ pw.println("<provider name> [onBind|onUnbind|locationTimeZoneEvent"
+ + " <location time zone event args>]");
+ pw.println();
+ pw.println("<provider name> = " + VALID_PROVIDER_NAMES);
+ pw.println("<location time zone event args> ="
+ + " [PERMANENT_FAILURE|UNCERTAIN|SUCCESS <time zone ids>*]");
+ }
+
+ /**
+ * Constructs a {@link SimulatedBinderProviderEvent} from the arguments of {@code shellCommand}.
+ */
+ static SimulatedBinderProviderEvent createFromArgs(ShellCommand shellCommand) {
+ String providerName = shellCommand.getNextArgRequired();
+ if (!VALID_PROVIDER_NAMES.contains(providerName)) {
+ throw new IllegalArgumentException("Unknown provider name=" + providerName);
+ }
+ String injectedEvent = shellCommand.getNextArgRequired();
+ switch (injectedEvent) {
+ case "onBind": {
+ return new SimulatedBinderProviderEvent(
+ providerName, INJECTED_EVENT_TYPE_ON_BIND, null);
+ }
+ case "onUnbind": {
+ return new SimulatedBinderProviderEvent(
+ providerName, INJECTED_EVENT_TYPE_ON_UNBIND, null);
+ }
+ case "locationTimeZoneEvent": {
+ LocationTimeZoneEvent event = parseLocationTimeZoneEventArgs(shellCommand);
+ return new SimulatedBinderProviderEvent(providerName,
+ INJECTED_EVENT_TYPE_LOCATION_TIME_ZONE_EVENT, event);
+ }
+ default: {
+ throw new IllegalArgumentException("Unknown simulated event type=" + injectedEvent);
+ }
+ }
+ }
+
+ private static LocationTimeZoneEvent parseLocationTimeZoneEventArgs(ShellCommand shellCommand) {
+ LocationTimeZoneEvent.Builder eventBuilder = new LocationTimeZoneEvent.Builder()
+ .setElapsedRealtimeNanos(SystemClock.elapsedRealtime())
+ .setUserHandle(UserHandle.of(ActivityManager.getCurrentUser()));
+
+ String eventTypeString = shellCommand.getNextArgRequired();
+ switch (eventTypeString.toUpperCase()) {
+ case "PERMANENT_FAILURE": {
+ eventBuilder.setEventType(EVENT_TYPE_PERMANENT_FAILURE);
+ break;
+ }
+ case "UNCERTAIN": {
+ eventBuilder.setEventType(EVENT_TYPE_UNCERTAIN);
+ break;
+ }
+ case "SUCCESS": {
+ eventBuilder.setEventType(EVENT_TYPE_SUCCESS)
+ .setTimeZoneIds(parseTimeZoneArgs(shellCommand));
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("Error: Unknown eventType: " + eventTypeString);
+ }
+ }
+ return eventBuilder.build();
+ }
+
+ private static List<String> parseTimeZoneArgs(ShellCommand shellCommand) {
+ List<String> timeZoneIds = new ArrayList<>();
+ String timeZoneId;
+ while ((timeZoneId = shellCommand.getNextArg()) != null) {
+ timeZoneIds.add(timeZoneId);
+ }
+ return timeZoneIds;
+ }
+
+ @Override
+ public String toString() {
+ return "SimulatedBinderProviderEvent{"
+ + "mProviderName=" + mProviderName
+ + ", mEventType=" + mEventType
+ + ", mLocationTimeZoneEvent=" + mLocationTimeZoneEvent
+ + '}';
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java
new file mode 100644
index 0000000..462bcab
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java
@@ -0,0 +1,118 @@
+/*
+ * 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.location.timezone;
+
+import static com.android.server.location.timezone.SimulatedBinderProviderEvent.INJECTED_EVENT_TYPE_LOCATION_TIME_ZONE_EVENT;
+import static com.android.server.location.timezone.SimulatedBinderProviderEvent.INJECTED_EVENT_TYPE_ON_BIND;
+import static com.android.server.location.timezone.SimulatedBinderProviderEvent.INJECTED_EVENT_TYPE_ON_UNBIND;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;
+import com.android.server.timezonedetector.ReferenceWithHistory;
+
+import java.util.Objects;
+
+/**
+ * A replacement for a real binder proxy for use during integration testing
+ * that can be used to inject simulated {@link LocationTimeZoneProviderProxy} behavior.
+ */
+class SimulatedLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
+
+ @GuardedBy("mProxyLock")
+ @NonNull private LocationTimeZoneProviderRequest mRequest;
+
+ @NonNull private ReferenceWithHistory<String> mLastEvent = new ReferenceWithHistory<>(50);
+
+ SimulatedLocationTimeZoneProviderProxy(
+ @NonNull Context context, @NonNull ThreadingDomain threadingDomain) {
+ super(context, threadingDomain);
+ mRequest = LocationTimeZoneProviderRequest.EMPTY_REQUEST;
+ }
+
+ void simulate(@NonNull SimulatedBinderProviderEvent event) {
+ switch (event.getEventType()) {
+ case INJECTED_EVENT_TYPE_ON_BIND: {
+ mLastEvent.set("Simulating onProviderBound(), event=" + event);
+ mThreadingDomain.post(this::onBindOnHandlerThread);
+ break;
+ }
+ case INJECTED_EVENT_TYPE_ON_UNBIND: {
+ mLastEvent.set("Simulating onProviderUnbound(), event=" + event);
+ mThreadingDomain.post(this::onUnbindOnHandlerThread);
+ break;
+ }
+ case INJECTED_EVENT_TYPE_LOCATION_TIME_ZONE_EVENT: {
+ if (!mRequest.getReportLocationTimeZone()) {
+ mLastEvent.set("Test event=" + event + " is testing an invalid case:"
+ + " reporting is off. mRequest=" + mRequest);
+ }
+ mLastEvent.set("Simulating LocationTimeZoneEvent, event=" + event);
+ handleLocationTimeZoneEvent(event.getLocationTimeZoneEvent());
+ break;
+ }
+ default: {
+ mLastEvent.set("Unknown simulated event type. event=" + event);
+ throw new IllegalArgumentException("Unknown simulated event type. event=" + event);
+ }
+ }
+ }
+
+ private void onBindOnHandlerThread() {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ mListener.onProviderBound();
+ }
+ }
+
+ private void onUnbindOnHandlerThread() {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ mListener.onProviderUnbound();
+ }
+ }
+
+ @Override
+ final void setRequest(@NonNull LocationTimeZoneProviderRequest request) {
+ mThreadingDomain.assertCurrentThread();
+
+ Objects.requireNonNull(request);
+ synchronized (mSharedLock) {
+ mLastEvent.set("Request received: " + request);
+ mRequest = request;
+ }
+ }
+
+ @Override
+ public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
+ synchronized (mSharedLock) {
+ ipw.println("mRequest=" + mRequest);
+ ipw.println("mLastEvent=" + mLastEvent);
+
+ ipw.increaseIndent();
+ ipw.println("Last event history:");
+ mLastEvent.dump(ipw);
+ ipw.decreaseIndent();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/ThreadingDomain.java b/services/core/java/com/android/server/location/timezone/ThreadingDomain.java
new file mode 100644
index 0000000..9b9c823
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/ThreadingDomain.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.timezone;
+
+import android.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class that can be used to enforce / indicate a set of components that need to share threading
+ * behavior such as a shared lock object and a common thread, with async execution support.
+ *
+ * <p>It is <em>not</em> essential that the object returned by {@link #getLockObject()} is only used
+ * when executing on the domain's thread, but users should be careful to avoid deadlocks when
+ * multiple locks / threads are in use. Generally sticking to a single thread / lock is safest.
+ */
+abstract class ThreadingDomain {
+
+ @NonNull private final Object mLockObject;
+
+ ThreadingDomain() {
+ mLockObject = new Object();
+ }
+
+ /**
+ * Returns the common lock object for this threading domain that can be used for synchronized ()
+ * blocks. The lock is unique to this threading domain.
+ */
+ @NonNull
+ Object getLockObject() {
+ return mLockObject;
+ }
+
+ /**
+ * Returns the Thread associated with this threading domain.
+ */
+ @NonNull
+ abstract Thread getThread();
+
+ /**
+ * Asserts the currently executing thread is the one associated with this threading domain.
+ * Generally useful for documenting expectations in the code. By asserting a single thread is
+ * being used within a set of components, a lot of races can be avoided.
+ */
+ void assertCurrentThread() {
+ Preconditions.checkArgument(Thread.currentThread() == getThread());
+ }
+
+ /**
+ * Execute the supplied runnable on the threading domain's thread.
+ */
+ abstract void post(@NonNull Runnable runnable);
+
+ /**
+ * Execute the supplied runnable on the threading domain's thread with a delay.
+ */
+ abstract void postDelayed(@NonNull Runnable runnable, long delayMillis);
+
+ abstract void postDelayed(Runnable r, Object token, long delayMillis);
+
+ abstract void removeQueuedRunnables(Object token);
+
+ /**
+ * Creates a new {@link SingleRunnableQueue} that can be used to ensure that (at most) a
+ * single runnable for a given purpose is ever queued. Create new ones for different purposes.
+ */
+ SingleRunnableQueue createSingleRunnableQueue() {
+ return new SingleRunnableQueue();
+ }
+
+ /**
+ * A class that allows up to one {@link Runnable} to be queued on the handler, i.e. calling any
+ * of the methods will cancel the execution of any previously queued / delayed runnable. All
+ * methods must be called from the {@link ThreadingDomain}'s thread.
+ */
+ final class SingleRunnableQueue {
+
+ /**
+ * Runs the supplied {@link Runnable} synchronously on the threading domain's thread,
+ * cancelling any queued but not-yet-executed {@link Runnable} previously added by this.
+ * This method must be called from the threading domain's thread.
+ */
+ void runSynchronously(Runnable r) {
+ cancel();
+ r.run();
+ }
+
+ /**
+ * Posts the supplied {@link Runnable} asynchronously and delayed on the threading domain
+ * handler thread, cancelling any queued but not-yet-executed {@link Runnable} previously
+ * added by this. This method must be called from the threading domain's thread.
+ */
+ void runDelayed(Runnable r, long delayMillis) {
+ cancel();
+ ThreadingDomain.this.postDelayed(r, this, delayMillis);
+ }
+
+ /**
+ * Cancels any queued but not-yet-executed {@link Runnable} previously added by this.
+ */
+ public void cancel() {
+ assertCurrentThread();
+ removeQueuedRunnables(this);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/util/LocationUsageLogger.java b/services/core/java/com/android/server/location/util/LocationUsageLogger.java
index 2e50d4f..a229964 100644
--- a/services/core/java/com/android/server/location/util/LocationUsageLogger.java
+++ b/services/core/java/com/android/server/location/util/LocationUsageLogger.java
@@ -49,7 +49,7 @@
* Log a location API usage event.
*/
public void logLocationApiUsage(int usageType, int apiInUse,
- String packageName, LocationRequest locationRequest,
+ String packageName, String provider, LocationRequest locationRequest,
boolean hasListener, boolean hasIntent,
Geofence geofence, boolean foreground) {
try {
@@ -64,22 +64,22 @@
usageType, apiInUse, packageName,
isLocationRequestNull
? LocationStatsEnums.PROVIDER_UNKNOWN
- : bucketizeProvider(locationRequest.getProvider()),
+ : bucketizeProvider(provider),
isLocationRequestNull
? LocationStatsEnums.QUALITY_UNKNOWN
: locationRequest.getQuality(),
isLocationRequestNull
? LocationStatsEnums.INTERVAL_UNKNOWN
- : bucketizeInterval(locationRequest.getInterval()),
+ : bucketizeInterval(locationRequest.getIntervalMillis()),
isLocationRequestNull
? LocationStatsEnums.DISTANCE_UNKNOWN
: bucketizeDistance(
- locationRequest.getSmallestDisplacement()),
- isLocationRequestNull ? 0 : locationRequest.getNumUpdates(),
+ locationRequest.getMinUpdateDistanceMeters()),
+ isLocationRequestNull ? 0 : locationRequest.getMaxUpdates(),
// only log expireIn for USAGE_STARTED
isLocationRequestNull || usageType == LocationStatsEnums.USAGE_ENDED
? LocationStatsEnums.EXPIRATION_UNKNOWN
- : bucketizeExpireIn(locationRequest.getExpireIn()),
+ : bucketizeExpireIn(locationRequest.getDurationMillis()),
getCallbackType(apiInUse, hasListener, hasIntent),
isGeofenceNull
? LocationStatsEnums.RADIUS_UNKNOWN
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index dbc725e..1b43db2 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -2581,11 +2581,11 @@
* make it consistent with current behaviour. It also allows ActivityManager to call
* unlockUser() with empty secret.
* 3. Once a user is migrated to have synthetic password, its value will never change, no matter
- * whether the user changes his lockscreen PIN or clear/reset it. When the user clears its
+ * whether the user changes their lockscreen PIN or clear/reset it. When the user clears its
* lockscreen PIN, we still maintain the existing synthetic password in a password blob
* protected by a default PIN.
* 4. The user SID is linked with synthetic password, but its cleared/re-created when the user
- * clears/re-creates his lockscreen PIN.
+ * clears/re-creates their lockscreen PIN.
*
*
* Different cases of calling this method:
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index e31bf3d..7ab8b27 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -149,15 +149,15 @@
}
/**
- * This class represents the master cryptographic secret for a given user (a.k.a synthietic
+ * This class represents the main cryptographic secret for a given user (a.k.a synthietic
* password). This secret is derived from the user's lockscreen credential or password escrow
* token. All other cryptograhic keys related to the user, including disk encryption key,
* keystore encryption key, gatekeeper auth key, vendor auth secret and others are directly
* derived from this token.
* <p>
- * The master secret associated with an authentication token is retrievable from
+ * The main secret associated with an authentication token is retrievable from
* {@link AuthenticationToken#getSyntheticPassword()} and the authentication token can be
- * reconsturcted from the master secret later with
+ * reconsturcted from the main secret later with
* {@link AuthenticationToken#recreateDirectly(byte[])}. The first time an authentication token
* is needed, it should be created with {@link AuthenticationToken#create()} so that the
* necessary escrow data ({@link #mEncryptedEscrowSplit0} and {@link #mEscrowSplit1}) is
@@ -166,7 +166,7 @@
* needs to securely store the secret returned from
* {@link AuthenticationToken#getEscrowSecret()}, and at the time of use, load the escrow data
* back with {@link AuthenticationToken#setEscrowData(byte[], byte[])} and then re-create the
- * master secret from the escrow secret via
+ * main secret from the escrow secret via
* {@link AuthenticationToken#recreateFromEscrow(byte[])}.
*/
static class AuthenticationToken {
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index d345029..8e93215 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -825,7 +825,7 @@
* Information about a full user and its corresponding managed profiles.
*
* <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate
- * them when he/she presses a media/volume button. So keeping media sessions for them in one
+ * them when they press a media/volume button. So keeping media sessions for them in one
* place makes more sense and increases the readability.</p>
* <p>The contents of this object is guarded by {@link #mLock}.
*/
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 48f1ddb..407cedf 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -72,11 +72,11 @@
}
/**
- * Informs that an appId has been added or removed from the temp-powersave-whitelist so that
+ * Informs that an appId has been added or removed from the temp-powersave-allowlist so that
* that network rules for that appId can be updated.
*
- * @param appId The appId which has been updated in the whitelist.
- * @param added Denotes whether the {@param appId} has been added or removed from the whitelist.
+ * @param appId The appId which has been updated in the allowlist.
+ * @param added Denotes whether the {@param appId} has been added or removed from the allowlist.
*/
public abstract void onTempPowerSaveWhitelistChange(int appId, boolean added);
@@ -107,7 +107,7 @@
public abstract void onAdminDataAvailable();
/**
- * Control if a UID should be whitelisted even if it's in app idle mode. Other restrictions may
+ * Control if a UID should be allowlisted even if it's in app idle mode. Other restrictions may
* still be in effect.
*/
public abstract void setAppIdleWhitelist(int uid, boolean shouldWhitelist);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index ded77b3..2acc60d 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -478,7 +478,7 @@
// and "battery saver" are not equivalent.
/**
- * UIDs that have been white-listed to always be able to have network access
+ * UIDs that have been allowlisted to always be able to have network access
* in power save mode, except device idle (doze) still applies.
* TODO: An int array might be sufficient
*/
@@ -486,7 +486,7 @@
private final SparseBooleanArray mPowerSaveWhitelistExceptIdleAppIds = new SparseBooleanArray();
/**
- * UIDs that have been white-listed to always be able to have network access
+ * UIDs that have been allowlisted to always be able to have network access
* in power save mode.
* TODO: An int array might be sufficient
*/
@@ -497,21 +497,21 @@
private final SparseBooleanArray mPowerSaveTempWhitelistAppIds = new SparseBooleanArray();
/**
- * UIDs that have been white-listed temporarily to be able to have network access despite being
+ * UIDs that have been allowlisted temporarily to be able to have network access despite being
* idle. Other power saving restrictions still apply.
*/
@GuardedBy("mUidRulesFirstLock")
private final SparseBooleanArray mAppIdleTempWhitelistAppIds = new SparseBooleanArray();
/**
- * UIDs that have been initially white-listed by system to avoid restricted background.
+ * UIDs that have been initially allowlisted by system to avoid restricted background.
*/
@GuardedBy("mUidRulesFirstLock")
private final SparseBooleanArray mDefaultRestrictBackgroundAllowlistUids =
new SparseBooleanArray();
/**
- * UIDs that have been initially white-listed by system to avoid restricted background,
+ * UIDs that have been initially allowlisted by system to avoid restricted background,
* but later revoked by user.
*/
@GuardedBy("mUidRulesFirstLock")
@@ -820,7 +820,7 @@
// ignored; both services live in system_server
}
- // listen for changes to power save whitelist
+ // listen for changes to power save allowlist
final IntentFilter whitelistFilter = new IntentFilter(
PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
mContext.registerReceiver(mPowerSaveWhitelistReceiver, whitelistFilter, null, mHandler);
@@ -3784,7 +3784,7 @@
}
// NOTE: since both fw_dozable and fw_powersave uses the same map
- // (mPowerSaveTempWhitelistAppIds) for whitelisting, we can reuse their logic in this method.
+ // (mPowerSaveTempWhitelistAppIds) for allowlisting, we can reuse their logic in this method.
@GuardedBy("mUidRulesFirstLock")
private void updateRulesForWhitelistedPowerSaveUL(boolean enabled, int chain,
SparseIntArray rules) {
@@ -3826,12 +3826,12 @@
}
/**
- * Returns whether a uid is whitelisted from power saving restrictions (eg: Battery Saver, Doze
+ * Returns whether a uid is allowlisted from power saving restrictions (eg: Battery Saver, Doze
* mode, and app idle).
*
* @param deviceIdleMode if true then we don't consider
* {@link #mPowerSaveWhitelistExceptIdleAppIds} for checking if the {@param uid} is
- * whitelisted.
+ * allowlisted.
*/
@GuardedBy("mUidRulesFirstLock")
private boolean isWhitelistedFromPowerSaveUL(int uid, boolean deviceIdleMode) {
@@ -3845,7 +3845,7 @@
}
// NOTE: since both fw_dozable and fw_powersave uses the same map
- // (mPowerSaveTempWhitelistAppIds) for whitelisting, we can reuse their logic in this method.
+ // (mPowerSaveTempWhitelistAppIds) for allowlisting, we can reuse their logic in this method.
@GuardedBy("mUidRulesFirstLock")
private void updateRulesForWhitelistedPowerSaveUL(int uid, boolean enabled, int chain) {
if (enabled) {
@@ -4104,7 +4104,7 @@
}
/**
- * Set whether or not an app should be whitelisted for network access while in app idle. Other
+ * Set whether or not an app should be allowlisted for network access while in app idle. Other
* power saving restrictions may still apply.
*/
@VisibleForTesting
@@ -4133,7 +4133,7 @@
}
}
- /** Return the list of UIDs currently in the app idle whitelist. */
+ /** Return the list of UIDs currently in the app idle allowlist. */
@VisibleForTesting
int[] getAppIdleWhitelist() {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
@@ -4153,7 +4153,7 @@
boolean isUidIdle(int uid) {
synchronized (mUidRulesFirstLock) {
if (mAppIdleTempWhitelistAppIds.get(uid)) {
- // UID is temporarily whitelisted.
+ // UID is temporarily allowlisted.
return false;
}
}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index d48570f..48ec9b4 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -465,7 +465,7 @@
}
private int reconcileSecondaryDexFiles(DexManager dm) {
- // TODO(calin): should we blacklist packages for which we fail to reconcile?
+ // TODO(calin): should we denylist packages for which we fail to reconcile?
for (String p : dm.getAllPackagesWithSecondaryDexFiles()) {
if (mAbortIdleOptimization.get()) {
return OPTIMIZE_ABORT_BY_JOB_SCHEDULER;
diff --git a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
index 784f701..da65fe2 100644
--- a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
+++ b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
@@ -204,7 +204,7 @@
//
// A typical message might look like this:
// type=1400 audit(0.0:521): avc: granted { execute } for comm="executable"
- // path="/data/data/com.dummy.app/executable" dev="sda13" ino=1655302
+ // path="/data/data/com.placeholder.app/executable" dev="sda13" ino=1655302
// scontext=u:r:untrusted_app_27:s0:c66,c257,c512,c768
// tcontext=u:object_r:app_data_file:s0:c66,c257,c512,c768 tclass=file
//
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 42e6d8f..fc02b34 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -193,7 +193,7 @@
String[] classLoaderContexts = DexoptUtils.getClassLoaderContexts(
pkg, sharedLibraries, pathsWithCode);
- // Sanity check that we do not call dexopt with inconsistent data.
+ // Validity check that we do not call dexopt with inconsistent data.
if (paths.size() != classLoaderContexts.length) {
String[] splitCodePaths = pkg.getSplitCodePaths();
throw new IllegalStateException("Inconsistent information "
@@ -676,7 +676,7 @@
int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
// Some apps are executed with restrictions on hidden API usage. If this app is one
// of them, pass a flag to dexopt to enable the same restrictions during compilation.
- // TODO we should pass the actual flag value to dexopt, rather than assuming blacklist
+ // TODO we should pass the actual flag value to dexopt, rather than assuming denylist
// TODO(b/135203078): This flag is no longer set as part of AndroidPackage
// and may not be preserved
int hiddenApiFlag = hiddenApiEnforcementPolicy == HIDDEN_API_ENFORCEMENT_DISABLED
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 87dedfe..48efa5c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -161,6 +161,7 @@
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -744,7 +745,7 @@
info.mode = params.mode;
info.installReason = params.installReason;
info.sizeBytes = params.sizeBytes;
- info.appPackageName = params.appPackageName;
+ info.appPackageName = mPackageName != null ? mPackageName : params.appPackageName;
if (includeIcon) {
info.appIcon = params.appIcon;
}
@@ -1599,8 +1600,8 @@
}
@GuardedBy("mLock")
- private @Nullable List<PackageInstallerSession> getChildSessionsLocked() {
- List<PackageInstallerSession> childSessions = null;
+ private @NonNull List<PackageInstallerSession> getChildSessionsLocked() {
+ List<PackageInstallerSession> childSessions = Collections.EMPTY_LIST;
if (isMultiPackage()) {
int size = mChildSessions.size();
childSessions = new ArrayList<>(size);
@@ -1611,6 +1612,12 @@
return childSessions;
}
+ @NonNull List<PackageInstallerSession> getChildSessions() {
+ synchronized (mLock) {
+ return getChildSessionsLocked();
+ }
+ }
+
/**
* Seal the session to prevent further modification.
*
@@ -2068,7 +2075,7 @@
if (isStaged()) {
// TODO(b/136257624): Remove this once all verification logic has been transferred out
// of StagingManager.
- mStagingManager.notifyPreRebootVerification_Apk_Complete(sessionId);
+ mStagingManager.notifyPreRebootVerification_Apk_Complete(this);
// TODO(b/136257624): We also need to destroy internals for verified staged session,
// otherwise file descriptors are never closed for verified staged session until reboot
return;
@@ -2350,6 +2357,17 @@
"Invalid filename: " + targetName);
}
+ // Yell loudly if installers drop attribute installLocation when apps explicitly set.
+ if (apk.installLocation != PackageInfo.INSTALL_LOCATION_UNSPECIFIED) {
+ final String installerPackageName = getInstallerPackageName();
+ if (installerPackageName != null
+ && (params.installLocation != apk.installLocation)) {
+ Slog.wtf(TAG, installerPackageName
+ + " drops manifest attribute android:installLocation in " + targetName
+ + " for " + mPackageName);
+ }
+ }
+
final File targetFile = new File(stageDir, targetName);
resolveAndStageFileLocked(addedFile, targetFile, apk.splitName);
@@ -2606,6 +2624,7 @@
DexMetadataHelper.buildDexMetadataPathForApk(targetFile.getName()));
stageFileLocked(dexMetadataFile, targetDexMetadataFile);
+ maybeStageFsveritySignatureLocked(dexMetadataFile, targetDexMetadataFile);
}
private static ApkChecksum[] createApkChecksums(String splitName,
@@ -2662,20 +2681,27 @@
}
@GuardedBy("mLock")
- private void inheritFileLocked(File origFile) {
- mResolvedInheritedFiles.add(origFile);
-
+ private void maybeInheritFsveritySignatureLocked(File origFile) {
// Inherit the fsverity signature file if present.
final File fsveritySignatureFile = new File(
VerityUtils.getFsveritySignatureFilePath(origFile.getPath()));
if (fsveritySignatureFile.exists()) {
mResolvedInheritedFiles.add(fsveritySignatureFile);
}
+ }
+
+ @GuardedBy("mLock")
+ private void inheritFileLocked(File origFile) {
+ mResolvedInheritedFiles.add(origFile);
+
+ maybeInheritFsveritySignatureLocked(origFile);
+
// Inherit the dex metadata if present.
final File dexMetadataFile =
DexMetadataHelper.findDexMetadataForFile(origFile);
if (dexMetadataFile != null) {
mResolvedInheritedFiles.add(dexMetadataFile);
+ maybeInheritFsveritySignatureLocked(dexMetadataFile);
}
// Inherit the digests if present.
final File digestsFile = ApkChecksums.findDigestsForFile(origFile);
@@ -3631,7 +3657,7 @@
}
private void cleanStageDir(List<PackageInstallerSession> childSessions) {
- if (childSessions != null) {
+ if (isMultiPackage()) {
for (PackageInstallerSession childSession : childSessions) {
childSession.cleanStageDir();
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 67412a8..2f26f39 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -176,6 +176,7 @@
import android.content.pm.IPackageDeleteObserver2;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.IPackageInstaller;
+import android.content.pm.IPackageLoadingProgressCallback;
import android.content.pm.IPackageManager;
import android.content.pm.IPackageManagerNative;
import android.content.pm.IPackageMoveObserver;
@@ -2142,7 +2143,7 @@
res.removedInfo.sendPackageRemovedBroadcasts(killApp);
}
- // Whitelist any restricted permissions first as some may be runtime
+ // Allowlist any restricted permissions first as some may be runtime
// that the installer requested to be granted at install time.
if (whitelistedRestrictedPermissions != null
&& !whitelistedRestrictedPermissions.isEmpty()) {
@@ -3094,7 +3095,10 @@
t.traceEnd();
t.traceBegin("read user settings");
- mFirstBoot = !mSettings.readLPw(mInjector.getUserManagerInternal().getUsers(false));
+ mFirstBoot = !mSettings.readLPw(mInjector.getUserManagerInternal().getUsers(
+ /* excludePartial= */ true,
+ /* excludeDying= */ false,
+ /* excludePreCreated= */ false));
t.traceEnd();
// Clean up orphaned packages for which the code path doesn't exist
@@ -3617,7 +3621,7 @@
ver.fingerprint = Build.FINGERPRINT;
}
- // Grandfather existing (installed before Q) non-system apps to hide
+ // Legacy existing (installed before Q) non-system apps to hide
// their icons in launcher.
if (!mOnlyCore && mIsPreQUpgrade) {
Slog.i(TAG, "Whitelisting all existing apps to hide their icons");
@@ -5423,6 +5427,23 @@
}
@Override
+ public int getTargetSdkVersion(String packageName) {
+ synchronized (mLock) {
+ final AndroidPackage pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ return -1;
+ }
+
+ final PackageSetting ps = getPackageSetting(pkg.getPackageName());
+ if (shouldFilterApplicationLocked(ps, Binder.getCallingUid(),
+ UserHandle.getCallingUserId())) {
+ return -1;
+ }
+ return pkg.getTargetSdkVersion();
+ }
+ }
+
+ @Override
public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId);
}
@@ -7481,7 +7502,7 @@
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
} else {
// we have an instant application locally, but, we can't admit that since
- // callers shouldn't be able to determine prior browsing. create a dummy
+ // callers shouldn't be able to determine prior browsing. create a placeholder
// auxiliary response so the downstream code behaves as if there's an
// instant application available externally. when it comes time to start
// the instant application, we'll do the right thing.
@@ -9531,10 +9552,16 @@
}
}
+ // The version of the application on the /system partition is less than or
+ // equal to the version on the /data partition. Throw an exception and use
+ // the application already installed on the /data partition.
if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
- // The version of the application on the /system partition is less than or
- // equal to the version on the /data partition. Throw an exception and use
- // the application already installed on the /data partition.
+ // In the case of a skipped package, commitReconciledScanResultLocked is not called to
+ // add the object to the "live" data structures, so this is the final mutation step
+ // for the package. Which means it needs to be finalized here to cache derived fields.
+ // This is relevant for cases where the disabled system package is used for flags or
+ // other metadata.
+ ((ParsedPackage) parsedPackage).hideAsFinal();
throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
+ " at " + parsedPackage.getPath() + " ignored: updated version "
+ pkgSetting.versionCode + " better than this "
@@ -10476,7 +10503,6 @@
private void clearAppDataLIF(AndroidPackage pkg, int userId, int flags) {
if (pkg == null) {
- Slog.wtf(TAG, "Package was null!", new Throwable());
return;
}
clearAppDataLeafLIF(pkg, userId, flags);
@@ -11101,7 +11127,7 @@
if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
// Exempt SharedUsers signed with the platform key.
// TODO(b/72378145) Fix this exemption. Force signature apps
- // to whitelist their privileged permissions just like other
+ // to allowlist their privileged permissions just like other
// priv-apps.
synchronized (mLock) {
PackageSetting platformPkgSetting = mSettings.mPackages.get("android");
@@ -15993,7 +16019,7 @@
String codePath = codeFile.getAbsolutePath();
if (mIncrementalManager != null && isIncrementalPath(codePath)) {
- mIncrementalManager.closeStorage(codePath);
+ mIncrementalManager.onPackageRemoved(codePath);
}
removeCodePathLI(codeFile);
@@ -16461,7 +16487,7 @@
/**
* A container of all data needed to commit a package to in-memory data structures and to disk.
- * TODO: move most of the data contained her into a PackageSetting for commit.
+ * TODO: move most of the data contained here into a PackageSetting for commit.
*/
private static class ReconciledPackage {
public final ReconcileRequest request;
@@ -17125,10 +17151,11 @@
& PackageManagerService.SCAN_AS_INSTANT_APP) != 0);
final AndroidPackage pkg = reconciledPkg.pkgSetting.pkg;
final String packageName = pkg.getPackageName();
+ final String codePath = pkg.getPath();
final boolean onIncremental = mIncrementalManager != null
- && isIncrementalPath(pkg.getPath());
+ && isIncrementalPath(codePath);
if (onIncremental) {
- IncrementalStorage storage = mIncrementalManager.openStorage(pkg.getPath());
+ IncrementalStorage storage = mIncrementalManager.openStorage(codePath);
if (storage == null) {
throw new IllegalArgumentException(
"Install: null storage for incremental package " + packageName);
@@ -17231,7 +17258,7 @@
// Notify BackgroundDexOptService that the package has been changed.
// If this is an update of a package which used to fail to compile,
- // BackgroundDexOptService will remove it from its blacklist.
+ // BackgroundDexOptService will remove it from its denylist.
// TODO: Layering violation
BackgroundDexOptService.notifyPackageChanged(packageName);
@@ -24199,15 +24226,13 @@
}
@Override
- public int getTargetSdkVersionForPackage(String packageName)
- throws RemoteException {
- int callingUser = UserHandle.getUserId(Binder.getCallingUid());
- ApplicationInfo info = getApplicationInfo(packageName, 0, callingUser);
- if (info == null) {
- throw new RemoteException(
- "Couldn't get ApplicationInfo for package " + packageName);
+ public int getTargetSdkVersionForPackage(String packageName) throws RemoteException {
+ int targetSdk = getTargetSdkVersion(packageName);
+ if (targetSdk != -1) {
+ return targetSdk;
}
- return info.targetSdkVersion;
+
+ throw new RemoteException("Couldn't get targetSdkVersion for package " + packageName);
}
@Override
@@ -25359,6 +25384,56 @@
public boolean isSuspendingAnyPackages(String suspendingPackage, int userId) {
return PackageManagerService.this.isSuspendingAnyPackages(suspendingPackage, userId);
}
+
+ @Override
+ public boolean registerInstalledLoadingProgressCallback(String packageName,
+ PackageManagerInternal.InstalledLoadingProgressCallback callback, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ mPermissionManager.enforceCrossUserPermission(
+ callingUid, userId, true, false,
+ "registerLoadingProgressCallback");
+ final PackageSetting ps;
+ synchronized (mLock) {
+ ps = mSettings.mPackages.get(packageName);
+ if (ps == null) {
+ Slog.w(TAG, "Failed registering loading progress callback. Package "
+ + packageName + " is not installed");
+ return false;
+ }
+ if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
+ Slog.w(TAG, "Failed registering loading progress callback. Package "
+ + packageName + " is not visible to the calling app");
+ return false;
+ }
+ // TODO(b/165841827): return false if package is fully loaded
+ }
+ if (mIncrementalManager == null) {
+ Slog.w(TAG,
+ "Failed registering loading progress callback. Incremental is not enabled");
+ return false;
+ }
+ return mIncrementalManager.registerCallback(ps.getPathString(),
+ (IPackageLoadingProgressCallback) callback.getBinder());
+ }
+
+ @Override
+ public boolean unregisterInstalledLoadingProgressCallback(String packageName,
+ PackageManagerInternal.InstalledLoadingProgressCallback callback) {
+ final PackageSetting ps;
+ synchronized (mLock) {
+ ps = mSettings.mPackages.get(packageName);
+ if (ps == null) {
+ Slog.w(TAG, "Failed unregistering loading progress callback. Package "
+ + packageName + " is not installed");
+ return false;
+ }
+ }
+ if (mIncrementalManager == null) {
+ return false;
+ }
+ return mIncrementalManager.unregisterCallback(ps.getPathString(),
+ (IPackageLoadingProgressCallback) callback.getBinder());
+ }
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 4d2e22a..d77683e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2753,7 +2753,7 @@
final InstallParams params = new InstallParams();
params.sessionParams = sessionParams;
- // Whitelist all permissions by default
+ // Allowlist all permissions by default
sessionParams.installFlags |= PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;
String opt;
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 4476e8a..276f880 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -42,7 +42,9 @@
* Settings data for a particular package we know about.
*/
public class PackageSetting extends PackageSettingBase {
- int appId;
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public int appId;
/**
* This can be null whenever a physical APK on device is missing. This can be the result of
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index f801702..9654307 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1512,6 +1512,7 @@
return;
}
str = new FileInputStream(userPackagesStateFile);
+ if (DEBUG_MU) Log.i(TAG, "Reading " + userPackagesStateFile);
}
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(str, StandardCharsets.UTF_8.name());
@@ -1956,9 +1957,13 @@
serializer.startTag(null, TAG_PACKAGE_RESTRICTIONS);
+ if (DEBUG_MU) Log.i(TAG, "Writing " + userPackagesStateFile);
for (final PackageSetting pkg : mPackages.values()) {
final PackageUserState ustate = pkg.readUserState(userId);
- if (DEBUG_MU) Log.i(TAG, " pkg=" + pkg.name + ", state=" + ustate.enabled);
+ if (DEBUG_MU) {
+ Log.i(TAG, " pkg=" + pkg.name + ", installed=" + ustate.installed
+ + ", state=" + ustate.enabled);
+ }
serializer.startTag(null, TAG_PACKAGE);
serializer.attribute(null, ATTR_NAME, pkg.name);
diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
index d4a02a9..eae89f0 100644
--- a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
+++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
@@ -497,7 +497,7 @@
Slog.d(TAG, "Temporarily adding " + shortcutId + " as dynamic");
}
// Add as a dynamic shortcut. In order for a shortcut to be dynamic, it must
- // have a target activity, so we set a dummy here. It's later removed
+ // have a target activity, so we set a placeholder here. It's later removed
// in deleteDynamicWithId().
if (original.getActivity() == null) {
original.setActivity(mService.getDummyMainActivity(appPackageName));
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 8412077..96f9982 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -4005,7 +4005,7 @@
}
/**
- * Create a dummy "main activity" component name which is used to create a dynamic shortcut
+ * Create a placeholder "main activity" component name which is used to create a dynamic shortcut
* with no main activity temporarily.
*/
@NonNull
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 462b215..d413213 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -60,7 +60,6 @@
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseIntArray;
import android.util.apk.ApkSignatureVerifier;
import com.android.internal.annotations.GuardedBy;
@@ -111,9 +110,6 @@
@GuardedBy("mStagedSessions")
private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();
- @GuardedBy("mStagedSessions")
- private final SparseIntArray mSessionRollbackIds = new SparseIntArray();
-
@GuardedBy("mFailedPackageNames")
private final List<String> mFailedPackageNames = new ArrayList<>();
private String mNativeFailureReason;
@@ -168,17 +164,6 @@
}
}
- private void updateStoredSession(@NonNull PackageInstallerSession sessionInfo) {
- synchronized (mStagedSessions) {
- PackageInstallerSession storedSession = mStagedSessions.get(sessionInfo.sessionId);
- // storedSession might be null if a call to abortSession was made before the session
- // is updated.
- if (storedSession != null) {
- mStagedSessions.put(sessionInfo.sessionId, sessionInfo);
- }
- }
- }
-
private void markBootCompleted() {
mApexManager.markBootCompleted();
}
@@ -236,13 +221,13 @@
+ " compatible with the one currently installed on device");
}
- private List<PackageInfo> submitSessionToApexService(
- @NonNull PackageInstallerSession session) throws PackageManagerException {
+ private List<PackageInfo> submitSessionToApexService(@NonNull PackageInstallerSession session,
+ int rollbackId) throws PackageManagerException {
final IntArray childSessionIds = new IntArray();
if (session.isMultiPackage()) {
- for (int id : session.getChildSessionIds()) {
- if (isApexSession(getStagedSession(id))) {
- childSessionIds.add(id);
+ for (PackageInstallerSession s : session.getChildSessions()) {
+ if (isApexSession(s)) {
+ childSessionIds.add(s.sessionId);
}
}
}
@@ -251,14 +236,11 @@
apexSessionParams.childSessionIds = childSessionIds.toArray();
if (session.params.installReason == PackageManager.INSTALL_REASON_ROLLBACK) {
apexSessionParams.isRollback = true;
- apexSessionParams.rollbackId = retrieveRollbackIdForCommitSession(session.sessionId);
+ apexSessionParams.rollbackId = rollbackId;
} else {
- synchronized (mStagedSessions) {
- int rollbackId = mSessionRollbackIds.get(session.sessionId, -1);
- if (rollbackId != -1) {
- apexSessionParams.hasRollbackEnabled = true;
- apexSessionParams.rollbackId = rollbackId;
- }
+ if (rollbackId != -1) {
+ apexSessionParams.hasRollbackEnabled = true;
+ apexSessionParams.rollbackId = rollbackId;
}
}
// submitStagedSession will throw a PackageManagerException if apexd verification fails,
@@ -356,18 +338,12 @@
if (!session.isMultiPackage()) {
return filter.test(session);
}
- synchronized (mStagedSessions) {
- final int[] childSessionIds = session.getChildSessionIds();
- for (int id : childSessionIds) {
- // Retrieve cached sessions matching ids.
- final PackageInstallerSession s = mStagedSessions.get(id);
- // Filter only the ones containing APEX.
- if (filter.test(s)) {
- return true;
- }
+ for (PackageInstallerSession s : session.getChildSessions()) {
+ if (filter.test(s)) {
+ return true;
}
- return false;
}
+ return false;
}
private boolean sessionContainsApex(@NonNull PackageInstallerSession session) {
@@ -423,19 +399,9 @@
private List<PackageInstallerSession> extractApexSessions(PackageInstallerSession session) {
List<PackageInstallerSession> apexSessions = new ArrayList<>();
if (session.isMultiPackage()) {
- List<PackageInstallerSession> childrenSessions = new ArrayList<>();
- synchronized (mStagedSessions) {
- for (int childSessionId : session.getChildSessionIds()) {
- PackageInstallerSession childSession = mStagedSessions.get(childSessionId);
- if (childSession != null) {
- childrenSessions.add(childSession);
- }
- }
- }
- for (int i = 0, size = childrenSessions.size(); i < size; i++) {
- final PackageInstallerSession childSession = childrenSessions.get(i);
- if (sessionContainsApex(childSession)) {
- apexSessions.add(childSession);
+ for (PackageInstallerSession s : session.getChildSessions()) {
+ if (sessionContainsApex(s)) {
+ apexSessions.add(s);
}
}
} else {
@@ -574,7 +540,7 @@
// failed when not in checkpoint mode, hence it is being processed separately.
Slog.d(TAG, "Found pending staged session " + session.sessionId + " still to "
+ "be verified, resuming pre-reboot verification");
- mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
+ mPreRebootVerificationHandler.startPreRebootVerification(session);
return;
}
}
@@ -782,13 +748,9 @@
// carrying over all the session parameters and unmarking them as staged. On commit the
// sessions will be installed atomically.
final List<PackageInstallerSession> childSessions = new ArrayList<>();
- synchronized (mStagedSessions) {
- final int[] childSessionIds = session.getChildSessionIds();
- for (int id : childSessionIds) {
- final PackageInstallerSession s = mStagedSessions.get(id);
- if (!isApexSession(s)) {
- childSessions.add(s);
- }
+ for (PackageInstallerSession s : session.getChildSessions()) {
+ if (!isApexSession(s)) {
+ childSessions.add(s);
}
}
if (childSessions.isEmpty()) {
@@ -834,14 +796,10 @@
if (!session.isMultiPackage()) {
return;
}
- final int[] childSessionIds = session.getChildSessionIds();
final Set<String> apkNames = new ArraySet<>();
- synchronized (mStagedSessions) {
- for (int id : childSessionIds) {
- final PackageInstallerSession s = mStagedSessions.get(id);
- if (!isApexSession(s)) {
- apkNames.add(s.getPackageName());
- }
+ for (PackageInstallerSession s : session.getChildSessions()) {
+ if (!isApexSession(s)) {
+ apkNames.add(s.getPackageName());
}
}
final List<PackageInstallerSession> apexSessions = extractApexSessions(session);
@@ -896,8 +854,7 @@
}
void commitSession(@NonNull PackageInstallerSession session) {
- updateStoredSession(session);
- mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
+ mPreRebootVerificationHandler.startPreRebootVerification(session);
}
private int getSessionIdForParentOrSelf(PackageInstallerSession session) {
@@ -1021,7 +978,6 @@
void abortSession(@NonNull PackageInstallerSession session) {
synchronized (mStagedSessions) {
mStagedSessions.remove(session.sessionId);
- mSessionRollbackIds.delete(session.sessionId);
}
}
@@ -1088,46 +1044,13 @@
|| apexSessionInfo.isRevertFailed;
}
- @GuardedBy("mStagedSessions")
- private boolean isMultiPackageSessionComplete(@NonNull PackageInstallerSession session) {
- // This method assumes that the argument is either a parent session of a multi-package
- // i.e. isMultiPackage() returns true, or that it is a child session, i.e.
- // hasParentSessionId() returns true.
- if (session.isMultiPackage()) {
- // Parent session of a multi-package group. Check that we restored all the children.
- for (int childSession : session.getChildSessionIds()) {
- if (mStagedSessions.get(childSession) == null) {
- return false;
- }
- }
- return true;
- }
- if (session.hasParentSessionId()) {
- PackageInstallerSession parent = mStagedSessions.get(session.getParentSessionId());
- if (parent == null) {
- return false;
- }
- return isMultiPackageSessionComplete(parent);
- }
- Slog.wtf(TAG, "Attempting to restore an invalid multi-package session.");
- return false;
- }
-
void restoreSession(@NonNull PackageInstallerSession session, boolean isDeviceUpgrading) {
PackageInstallerSession sessionToResume = session;
synchronized (mStagedSessions) {
mStagedSessions.append(session.sessionId, session);
- // For multi-package sessions, we don't know in which order they will be restored. We
- // need to wait until we have restored all the session in a group before restoring them.
- if (session.isMultiPackage() || session.hasParentSessionId()) {
- if (!isMultiPackageSessionComplete(session)) {
- // Still haven't recovered all sessions of the group, return.
- return;
- }
- // Group recovered, find the parent if necessary and resume the installation.
- if (session.hasParentSessionId()) {
- sessionToResume = mStagedSessions.get(session.getParentSessionId());
- }
+ if (session.hasParentSessionId()) {
+ // Only parent sessions can be restored
+ return;
}
}
// The preconditions used during pre-reboot verification might have changed when device
@@ -1163,7 +1086,7 @@
if (!session.isStagedSessionReady()) {
// The framework got restarted before the pre-reboot verification could complete,
// restart the verification.
- mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
+ mPreRebootVerificationHandler.startPreRebootVerification(session);
} else {
// Session had already being marked ready. Start the checks to verify if there is any
// follow-up work.
@@ -1251,13 +1174,13 @@
// TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all
// verification logic is extracted out of StagingManager into PMS, we can remove
// this.
- void notifyPreRebootVerification_Apk_Complete(int sessionId) {
- mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(sessionId);
+ void notifyPreRebootVerification_Apk_Complete(@NonNull PackageInstallerSession session) {
+ mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(session);
}
private final class PreRebootVerificationHandler extends Handler {
- // Hold session ids before handler gets ready to do the verification.
- private IntArray mPendingSessionIds;
+ // Hold sessions before handler gets ready to do the verification.
+ private List<PackageInstallerSession> mPendingSessions;
private boolean mIsReady;
PreRebootVerificationHandler(Looper looper) {
@@ -1286,12 +1209,8 @@
@Override
public void handleMessage(Message msg) {
final int sessionId = msg.arg1;
- final PackageInstallerSession session = getStagedSession(sessionId);
- if (session == null) {
- Slog.wtf(TAG, "Session disappeared in the middle of pre-reboot verification: "
- + sessionId);
- return;
- }
+ final int rollbackId = msg.arg2;
+ final PackageInstallerSession session = (PackageInstallerSession) msg.obj;
if (session.isDestroyed() || session.isStagedSessionFailed()) {
// No point in running verification on a destroyed/failed session
onPreRebootVerificationComplete(session);
@@ -1302,7 +1221,7 @@
handlePreRebootVerification_Start(session);
break;
case MSG_PRE_REBOOT_VERIFICATION_APEX:
- handlePreRebootVerification_Apex(session);
+ handlePreRebootVerification_Apex(session, rollbackId);
break;
case MSG_PRE_REBOOT_VERIFICATION_APK:
handlePreRebootVerification_Apk(session);
@@ -1316,28 +1235,31 @@
// Notify the handler that system is ready, and reschedule the pre-reboot verifications.
private synchronized void readyToStart() {
mIsReady = true;
- if (mPendingSessionIds != null) {
- for (int i = 0; i < mPendingSessionIds.size(); i++) {
- startPreRebootVerification(mPendingSessionIds.get(i));
+ if (mPendingSessions != null) {
+ for (int i = 0; i < mPendingSessions.size(); i++) {
+ PackageInstallerSession session = mPendingSessions.get(i);
+ startPreRebootVerification(session);
}
- mPendingSessionIds = null;
+ mPendingSessions = null;
}
}
// Method for starting the pre-reboot verification
- private synchronized void startPreRebootVerification(int sessionId) {
+ private synchronized void startPreRebootVerification(
+ @NonNull PackageInstallerSession session) {
if (!mIsReady) {
- if (mPendingSessionIds == null) {
- mPendingSessionIds = new IntArray();
+ if (mPendingSessions == null) {
+ mPendingSessions = new ArrayList<>();
}
- mPendingSessionIds.add(sessionId);
+ mPendingSessions.add(session);
return;
}
- PackageInstallerSession session = getStagedSession(sessionId);
- if (session != null && session.notifyStagedStartPreRebootVerification()) {
+ if (session.notifyStagedStartPreRebootVerification()) {
+ int sessionId = session.sessionId;
Slog.d(TAG, "Starting preRebootVerification for session " + sessionId);
- obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget();
+ obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, -1, session)
+ .sendToTarget();
}
}
@@ -1359,24 +1281,31 @@
session.notifyStagedEndPreRebootVerification();
}
- private void notifyPreRebootVerification_Start_Complete(int sessionId) {
- obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APEX, sessionId, 0).sendToTarget();
+ private void notifyPreRebootVerification_Start_Complete(
+ @NonNull PackageInstallerSession session, int rollbackId) {
+ obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APEX, session.sessionId, rollbackId, session)
+ .sendToTarget();
}
- private void notifyPreRebootVerification_Apex_Complete(int sessionId) {
- obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APK, sessionId, 0).sendToTarget();
+ private void notifyPreRebootVerification_Apex_Complete(
+ @NonNull PackageInstallerSession session) {
+ obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APK, session.sessionId, -1, session)
+ .sendToTarget();
}
- private void notifyPreRebootVerification_Apk_Complete(int sessionId) {
- obtainMessage(MSG_PRE_REBOOT_VERIFICATION_END, sessionId, 0).sendToTarget();
+ private void notifyPreRebootVerification_Apk_Complete(
+ @NonNull PackageInstallerSession session) {
+ obtainMessage(MSG_PRE_REBOOT_VERIFICATION_END, session.sessionId, -1, session)
+ .sendToTarget();
}
/**
- * A dummy state for starting the pre reboot verification.
+ * A placeholder state for starting the pre reboot verification.
*
* See {@link PreRebootVerificationHandler} to see all nodes of pre reboot verification
*/
private void handlePreRebootVerification_Start(@NonNull PackageInstallerSession session) {
+ int rollbackId = -1;
if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
// If rollback is enabled for this session, we call through to the RollbackManager
// with the list of sessions it must enable rollback for. Note that
@@ -1386,19 +1315,21 @@
try {
// NOTE: To stay consistent with the non-staged install flow, we don't fail the
// entire install if rollbacks can't be enabled.
- int rollbackId = rm.notifyStagedSession(session.sessionId);
- if (rollbackId != -1) {
- synchronized (mStagedSessions) {
- mSessionRollbackIds.put(session.sessionId, rollbackId);
- }
- }
+ rollbackId = rm.notifyStagedSession(session.sessionId);
} catch (RuntimeException re) {
Slog.e(TAG, "Failed to notifyStagedSession for session: "
+ session.sessionId, re);
}
+ } else if (session.params.installReason == PackageManager.INSTALL_REASON_ROLLBACK) {
+ try {
+ rollbackId = retrieveRollbackIdForCommitSession(session.sessionId);
+ } catch (PackageManagerException e) {
+ onPreRebootVerificationFailure(session, e.error, e.getMessage());
+ return;
+ }
}
- notifyPreRebootVerification_Start_Complete(session.sessionId);
+ notifyPreRebootVerification_Start_Complete(session, rollbackId);
}
/**
@@ -1409,7 +1340,8 @@
* <li>validates signatures of apex files</li>
* </ul></p>
*/
- private void handlePreRebootVerification_Apex(@NonNull PackageInstallerSession session) {
+ private void handlePreRebootVerification_Apex(
+ @NonNull PackageInstallerSession session, int rollbackId) {
final boolean hasApex = sessionContainsApex(session);
// APEX checks. For single-package sessions, check if they contain an APEX. For
@@ -1417,7 +1349,7 @@
if (hasApex) {
final List<PackageInfo> apexPackages;
try {
- apexPackages = submitSessionToApexService(session);
+ apexPackages = submitSessionToApexService(session, rollbackId);
for (int i = 0, size = apexPackages.size(); i < size; i++) {
validateApexSignature(apexPackages.get(i));
}
@@ -1431,17 +1363,17 @@
packageManagerInternal.pruneCachedApksInApex(apexPackages);
}
- notifyPreRebootVerification_Apex_Complete(session.sessionId);
+ notifyPreRebootVerification_Apex_Complete(session);
}
/**
* Pre-reboot verification state for apk files. Session is sent to
* {@link PackageManagerService} for verification and it notifies back the result via
- * {@link #notifyPreRebootVerification_Apk_Complete(int)}
+ * {@link #notifyPreRebootVerification_Apk_Complete}
*/
private void handlePreRebootVerification_Apk(@NonNull PackageInstallerSession session) {
if (!sessionContainsApk(session)) {
- notifyPreRebootVerification_Apk_Complete(session.sessionId);
+ notifyPreRebootVerification_Apk_Complete(session);
return;
}
session.verifyStagedSession();
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 4aaa8a5..0f00579 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -765,8 +765,6 @@
return null;
}
- // TODO(b/157921703): replace by getAliveUsers() or remove (so callers
- // explicitly call the 3-booleans version)
public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */
true);
@@ -5176,8 +5174,14 @@
@Override
public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
- return UserManagerService.this.getUsersInternal(/*excludePartial= */ true,
- excludeDying, /* excludePreCreated= */ true);
+ return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */ true);
+ }
+
+ @Override
+ public @NonNull List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying,
+ boolean excludePreCreated) {
+ return UserManagerService.this.getUsersInternal(excludePartial, excludeDying,
+ excludePreCreated);
}
@Override
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index f7e9e34..87a883c 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -726,7 +726,7 @@
case android.provider.Settings.Secure.ALWAYS_ON_VPN_APP:
case android.provider.Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN:
case android.provider.Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST:
- // Whitelist system uid (ConnectivityService) and root uid to change always-on vpn
+ // Allowlist system uid (ConnectivityService) and root uid to change always-on vpn
final int appId = UserHandle.getAppId(callingUid);
if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID) {
return false;
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index b95404f..f058ad9 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -48,41 +48,41 @@
/**
* Responsible for un/installing system packages based on user type.
*
- * <p>Uses the SystemConfig's install-in-user-type whitelist;
+ * <p>Uses the SystemConfig's install-in-user-type allowlist;
* see {@link SystemConfig#getAndClearPackageToUserTypeWhitelist} and
* {@link SystemConfig#getAndClearPackageToUserTypeBlacklist}.
*
* <p>If {@link #isEnforceMode()} is false, then all system packages are always installed for all
* users. The following applies when it is true.
*
- * <p>Any package can be in one of three states in the {@code SystemConfig} whitelist
+ * <p>Any package can be in one of three states in the {@code SystemConfig} allowlist
* <ol>
- * <li>Explicitly blacklisted for a particular user type</li>
- * <li>Explicitly whitelisted for a particular user type</li>
- * <li>Not mentioned at all, for any user type (neither whitelisted nor blacklisted)</li>
+ * <li>Explicitly denylisted for a particular user type</li>
+ * <li>Explicitly allowlisted for a particular user type</li>
+ * <li>Not mentioned at all, for any user type (neither allowlisted nor denylisted)</li>
* </ol>
*
- * <p>Blacklisting always takes precedence - if a package is blacklisted for a particular user,
- * it won't be installed on that type of user (even if it is also whitelisted for that user).
- * Next comes whitelisting - if it is whitelisted for a particular user, it will be installed on
- * that type of user (as long as it isn't blacklisted).
- * Finally, if the package is not mentioned at all (i.e. neither whitelisted nor blacklisted for
+ * <p>Denylisting always takes precedence - if a package is denylisted for a particular user,
+ * it won't be installed on that type of user (even if it is also allowlisted for that user).
+ * Next comes allowlisting - if it is allowlisted for a particular user, it will be installed on
+ * that type of user (as long as it isn't denylisted).
+ * Finally, if the package is not mentioned at all (i.e. neither allowlisted nor denylisted for
* any user types) in the SystemConfig 'install-in-user-type' lists
* then:
* <ul>
- * <li>If {@link #isImplicitWhitelistMode()}, the package is implicitly treated as whitelisted
+ * <li>If {@link #isImplicitWhitelistMode()}, the package is implicitly treated as allowlisted
* for <b>all</b> users</li>
* <li>Otherwise, if {@link #isImplicitWhitelistSystemMode()}, the package is implicitly treated
- * as whitelisted for the <b>{@link UserHandle#USER_SYSTEM}</b> user (not other users),
+ * as allowlisted for the <b>{@link UserHandle#USER_SYSTEM}</b> user (not other users),
* which is useful for local development purposes</li>
- * <li>Otherwise, the package is implicitly treated as blacklisted for all users</li>
+ * <li>Otherwise, the package is implicitly treated as denylisted for all users</li>
* </ul>
*
* <p>Packages are only installed/uninstalled by this mechanism when a new user is created or during
* an update. In the case of updates:<ul>
- * <li>new packages are (un)installed per the whitelist/blacklist</li>
- * <li>pre-existing installed blacklisted packages are never uninstalled</li>
- * <li>pre-existing not-installed whitelisted packages are only installed if the reason why they
+ * <li>new packages are (un)installed per the allowlist/denylist</li>
+ * <li>pre-existing installed denylisted packages are never uninstalled</li>
+ * <li>pre-existing not-installed allowlisted packages are only installed if the reason why they
* had been previously uninstalled was due to UserSystemPackageInstaller</li>
* </ul>
*
@@ -98,14 +98,14 @@
private static final boolean DEBUG = false;
/**
- * System Property whether to only install system packages on a user if they're whitelisted for
+ * System Property whether to only install system packages on a user if they're allowlisted for
* that user type. These are flags and can be freely combined.
* <ul>
- * <li> 0 - disable whitelist (install all system packages; no logging)</li>
- * <li> 1 - enforce (only install system packages if they are whitelisted)</li>
- * <li> 2 - log (log non-whitelisted packages)</li>
- * <li> 4 - for all users: implicitly whitelist any package not mentioned in the whitelist</li>
- * <li> 8 - for SYSTEM: implicitly whitelist any package not mentioned in the whitelist</li>
+ * <li> 0 - disable allowlist (install all system packages; no logging)</li>
+ * <li> 1 - enforce (only install system packages if they are allowlisted)</li>
+ * <li> 2 - log (log non-allowlisted packages)</li>
+ * <li> 4 - for all users: implicitly allowlist any package not mentioned in the allowlist</li>
+ * <li> 8 - for SYSTEM: implicitly allowlist any package not mentioned in the allowlist</li>
* <li> 16 - ignore OTAs (don't install system packages during OTAs)</li>
* <li>-1 - use device default (as defined in res/res/values/config.xml)</li>
* </ul>
@@ -146,7 +146,7 @@
* then mWhitelistedPackagesForUserTypes.get("pkg1") will be a Long whose
* bit in position 3 will equal 1.
* <p>
- * Packages that are whitelisted, but then blacklisted so that they aren't to be installed on
+ * Packages that are allowlisted, but then denylisted so that they aren't to be installed on
* any user, are purposefully still present in this list.
*/
private final ArrayMap<String, Long> mWhitelistedPackagesForUserTypes;
@@ -186,10 +186,10 @@
/**
* During OTAs and first boot, install/uninstall all system packages for all users based on the
- * user's user type and the SystemConfig whitelist.
+ * user's user type and the SystemConfig allowlist.
* We do NOT uninstall packages during an OTA though.
*
- * This is responsible for enforcing the whitelist for pre-existing users (i.e. USER_SYSTEM);
+ * This is responsible for enforcing the allowlist for pre-existing users (i.e. USER_SYSTEM);
* enforcement for new users is done when they are created in UserManagerService.createUser().
*
* @param preExistingPackages list of packages on the device prior to the upgrade. Cannot be
@@ -267,7 +267,7 @@
}
/**
- * Checks whether the system packages and the mWhitelistedPackagesForUserTypes whitelist are
+ * Checks whether the system packages and the mWhitelistedPackagesForUserTypes allowlist are
* in 1-to-1 correspondence.
*/
private void checkWhitelistedSystemPackages(@PackageWhitelistMode int mode) {
@@ -276,7 +276,7 @@
}
Slog.v(TAG, "Checking that all system packages are whitelisted.");
- // Check whether all whitelisted packages are indeed on the system.
+ // Check whether all allowlisted packages are indeed on the system.
final List<String> warnings = getPackagesWhitelistWarnings();
final int numberWarnings = warnings.size();
if (numberWarnings == 0) {
@@ -290,7 +290,7 @@
}
}
- // Check whether all system packages are indeed whitelisted.
+ // Check whether all system packages are indeed allowlisted.
if (isImplicitWhitelistMode(mode) && !isLogMode(mode)) {
return;
}
@@ -318,7 +318,7 @@
}
/**
- * Gets packages that are listed in the whitelist XML but are not present on the system image.
+ * Gets packages that are listed in the allowlist XML but are not present on the system image.
*/
@NonNull
private List<String> getPackagesWhitelistWarnings() {
@@ -326,7 +326,7 @@
final List<String> warnings = new ArrayList<>();
final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
- // Check whether all whitelisted packages are indeed on the system.
+ // Check whether all allowlisted packages are indeed on the system.
final String notPresentFmt = "%s is whitelisted but not present.";
final String notSystemFmt = "%s is whitelisted and present but not a system package.";
final String overlayPackageFmt = "%s is whitelisted but it's auto-generated RRO package.";
@@ -344,7 +344,7 @@
}
/**
- * Gets packages that are not listed in the whitelist XMLs when they should be.
+ * Gets packages that are not listed in the allowlist XMLs when they should be.
*/
@NonNull
private List<String> getPackagesWhitelistErrors(@PackageWhitelistMode int mode) {
@@ -356,7 +356,7 @@
final Set<String> allWhitelistedPackages = getWhitelistedSystemPackages();
final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
- // Check whether all system packages are indeed whitelisted.
+ // Check whether all system packages are indeed allowlisted.
final String logMessageFmt = "System package %s is not whitelisted using "
+ "'install-in-user-type' in SystemConfig for any user types!";
pmInt.forEachPackage(pkg -> {
@@ -371,7 +371,7 @@
return errors;
}
- /** Whether to only install system packages in new users for which they are whitelisted. */
+ /** Whether to only install system packages in new users for which they are allowlisted. */
boolean isEnforceMode() {
return isEnforceMode(getWhitelistMode());
}
@@ -382,7 +382,7 @@
* If in this mode, old system packages will not be installed on pre-existing users during OTAs.
* Any system packages that had not been installed at the time of the user's creation,
* due to {@link UserSystemPackageInstaller}'s previous actions, will therefore continue to
- * remain uninstalled, even if the whitelist (or enforcement mode) now declares that they should
+ * remain uninstalled, even if the allowlist (or enforcement mode) now declares that they should
* be.
*/
boolean isIgnoreOtaMode() {
@@ -390,23 +390,23 @@
}
/**
- * Whether to log a warning concerning potential problems with the user-type package whitelist.
+ * Whether to log a warning concerning potential problems with the user-type package allowlist.
*/
boolean isLogMode() {
return isLogMode(getWhitelistMode());
}
/**
- * Whether to treat all packages that are not mentioned at all in the whitelist to be implicitly
- * whitelisted for all users.
+ * Whether to treat all packages that are not mentioned at all in the allowlist to be implicitly
+ * allowlisted for all users.
*/
boolean isImplicitWhitelistMode() {
return isImplicitWhitelistMode(getWhitelistMode());
}
/**
- * Whether to treat all packages that are not mentioned at all in the whitelist to be implicitly
- * whitelisted for the SYSTEM user.
+ * Whether to treat all packages that are not mentioned at all in the allowlist to be implicitly
+ * allowlisted for the SYSTEM user.
*/
boolean isImplicitWhitelistSystemMode() {
return isImplicitWhitelistSystemMode(getWhitelistMode());
@@ -493,7 +493,7 @@
/**
* Gets the system package names that should be installed on users of the given user type, as
- * determined by SystemConfig, the whitelist mode, and the apps actually on the device.
+ * determined by SystemConfig, the allowlist mode, and the apps actually on the device.
* Names are the {@link PackageParser.Package#packageName}, not necessarily the manifest names.
*
* Returns null if all system packages should be installed (due to enforce-mode being off).
@@ -515,7 +515,7 @@
}
if (shouldInstallPackage(pkg, mWhitelistedPackagesForUserTypes,
whitelistedPackages, implicitlyWhitelist)) {
- // Although the whitelist uses manifest names, this function returns packageNames.
+ // Although the allowlist uses manifest names, this function returns packageNames.
installPackages.add(pkg.getPackageName());
}
});
@@ -524,7 +524,7 @@
/**
* Returns whether the given system package should be installed on the given user, based on the
- * the given whitelist of system packages.
+ * the given allowlist of system packages.
*
* @param sysPkg the system package. Must be a system package; no verification for this is done.
* @param userTypeWhitelist map of package manifest names to user types on which they should be
@@ -534,7 +534,7 @@
* <b>particular</b> user. This must be consistent with userTypeWhitelist,
* but is passed in separately to avoid repeatedly calculating it from
* userTypeWhitelist.
- * @param implicitlyWhitelist whether non-mentioned packages are implicitly whitelisted.
+ * @param implicitlyWhitelist whether non-mentioned packages are implicitly allowlisted.
*/
@VisibleForTesting
static boolean shouldInstallPackage(AndroidPackage sysPkg,
@@ -557,7 +557,7 @@
}
/**
- * Gets the package manifest names that are whitelisted for users of the given user type,
+ * Gets the package manifest names that are allowlisted for users of the given user type,
* as determined by SystemConfig.
*/
@VisibleForTesting
@@ -576,9 +576,9 @@
/**
* Set of package manifest names that are included anywhere in the package-to-user-type
- * whitelist, as determined by SystemConfig.
+ * allowlist, as determined by SystemConfig.
*
- * Packages that are whitelisted, but then blacklisted so that they aren't to be installed on
+ * Packages that are allowlisted, but then denylisted so that they aren't to be installed on
* any user, are still present in this list, since that is a valid scenario (e.g. if an OEM
* completely blacklists an AOSP app).
*/
@@ -596,9 +596,9 @@
* To enforce this:
* <ul>
* <li>Illegal user types are ignored.</li>
- * <li>Packages that never whitelisted at all (even if they are explicitly blacklisted) are
+ * <li>Packages that never allowlisted at all (even if they are explicitly denylisted) are
* ignored.</li>
- * <li>Packages that are blacklisted whenever they are whitelisted will be stored with the
+ * <li>Packages that are denylisted whenever they are allowlisted will be stored with the
* value 0 (since this is a valid scenario, e.g. if an OEM completely blacklists an
* AOSP app).</li>
* </ul>
@@ -614,7 +614,7 @@
sysConfig.getAndClearPackageToUserTypeWhitelist();
// result maps packageName -> userTypes on which the package should be installed.
final ArrayMap<String, Long> result = new ArrayMap<>(whitelist.size() + 1);
- // First, do the whitelisted user types.
+ // First, do the allowlisted user types.
for (int i = 0; i < whitelist.size(); i++) {
final String pkgName = whitelist.keyAt(i).intern();
final long typesBitSet = getTypesBitSet(whitelist.valueAt(i), baseTypeBitSets);
@@ -622,7 +622,7 @@
result.put(pkgName, typesBitSet);
}
}
- // Then, un-whitelist any blacklisted user types.
+ // Then, un-allowlist any denylisted user types.
final ArrayMap<String, Set<String>> blacklist =
sysConfig.getAndClearPackageToUserTypeBlacklist();
for (int i = 0; i < blacklist.size(); i++) {
@@ -632,7 +632,7 @@
if (typesBitSet != null) {
result.put(pkgName, typesBitSet & ~nonTypesBitSet);
} else if (nonTypesBitSet != 0) {
- // Package was never whitelisted but is validly blacklisted.
+ // Package was never allowlisted but is validly denylisted.
result.put(pkgName, 0L);
}
}
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 587cb825..3712723 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -185,7 +185,7 @@
return;
}
- // Sanity checks on the arguments.
+ // Validity checks on the arguments.
Objects.requireNonNull(callback);
boolean bootImageProfile = profileType == ArtManager.PROFILE_BOOT_IMAGE;
diff --git a/services/core/java/com/android/server/pm/dex/DexoptUtils.java b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
index fa01836..fc88af9 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptUtils.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
@@ -85,7 +85,7 @@
// The application has splits. Compute their class loader contexts.
- // First, cache the relative paths of the splits and do some sanity checks
+ // First, cache the relative paths of the splits and do some validity checks
String[] splitRelativeCodePaths = getSplitRelativeCodePaths(pkg);
// The splits have an implicit dependency on the base apk.
@@ -407,7 +407,7 @@
for (int i = 0; i < splitRelativeCodePaths.length; i++) {
File pathFile = new File(splitCodePaths[i]);
splitRelativeCodePaths[i] = pathFile.getName();
- // Sanity check that the base paths of the splits are all the same.
+ // Validity check that the base paths of the splits are all the same.
String basePath = pathFile.getParent();
if (!basePath.equals(baseCodePath)) {
Slog.wtf(TAG, "Split paths have different base paths: " + basePath + " and " +
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index d695a01..2d2e72a 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -19,6 +19,7 @@
import android.annotation.CheckResult;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.apex.ApexInfo;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -56,6 +57,7 @@
import com.android.server.pm.PackageSetting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.pkg.PackageStateUnserialized;
import libcore.util.EmptyArray;
@@ -218,7 +220,9 @@
}
ApplicationInfo info = PackageInfoWithoutStateUtils.generateApplicationInfoUnchecked(pkg,
- flags, state, userId);
+ flags, state, userId, false /* assignUserFields */);
+
+ initForUser(info, pkg, userId);
if (pkgSetting != null) {
// TODO(b/135203078): Remove PackageParser1/toAppInfoWithoutState and clean all this up
@@ -349,7 +353,11 @@
if (i == null) return null;
InstrumentationInfo info =
- PackageInfoWithoutStateUtils.generateInstrumentationInfo(i, pkg, flags, userId);
+ PackageInfoWithoutStateUtils.generateInstrumentationInfo(i, pkg, flags, userId,
+ false /* assignUserFields */);
+
+ initForUser(info, pkg, userId);
+
if (info == null) {
return null;
}
@@ -496,6 +504,90 @@
// @formatter:on
}
+ private static void initForUser(ApplicationInfo output, AndroidPackage input,
+ @UserIdInt int userId) {
+ PackageImpl pkg = ((PackageImpl) input);
+ String packageName = input.getPackageName();
+ output.uid = UserHandle.getUid(userId, UserHandle.getAppId(input.getUid()));
+
+ if ("android".equals(packageName)) {
+ output.dataDir = PackageInfoWithoutStateUtils.SYSTEM_DATA_PATH;
+ return;
+ }
+
+ // For performance reasons, all these paths are built as strings
+ if (userId == UserHandle.USER_SYSTEM) {
+ output.credentialProtectedDataDir =
+ pkg.getBaseAppDataCredentialProtectedDirForSystemUser() + packageName;
+ output.deviceProtectedDataDir =
+ pkg.getBaseAppDataDeviceProtectedDirForSystemUser() + packageName;
+ } else {
+ // Convert /data/user/0/ -> /data/user/1/com.example.app
+ String userIdString = String.valueOf(userId);
+ int credentialLength = pkg.getBaseAppDataCredentialProtectedDirForSystemUser().length();
+ output.credentialProtectedDataDir =
+ new StringBuilder(pkg.getBaseAppDataCredentialProtectedDirForSystemUser())
+ .replace(credentialLength - 2, credentialLength - 1, userIdString)
+ .append(packageName)
+ .toString();
+ int deviceLength = pkg.getBaseAppDataDeviceProtectedDirForSystemUser().length();
+ output.deviceProtectedDataDir =
+ new StringBuilder(pkg.getBaseAppDataDeviceProtectedDirForSystemUser())
+ .replace(deviceLength - 2, deviceLength - 1, userIdString)
+ .append(packageName)
+ .toString();
+ }
+
+ if (input.isDefaultToDeviceProtectedStorage()
+ && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
+ output.dataDir = output.deviceProtectedDataDir;
+ } else {
+ output.dataDir = output.credentialProtectedDataDir;
+ }
+ }
+
+ // This duplicates the ApplicationInfo variant because it uses field assignment and the classes
+ // don't inherit from each other, unfortunately. Consolidating logic would introduce overhead.
+ private static void initForUser(InstrumentationInfo output, AndroidPackage input,
+ @UserIdInt int userId) {
+ PackageImpl pkg = ((PackageImpl) input);
+ String packageName = input.getPackageName();
+ if ("android".equals(packageName)) {
+ output.dataDir = PackageInfoWithoutStateUtils.SYSTEM_DATA_PATH;
+ return;
+ }
+
+ // For performance reasons, all these paths are built as strings
+ if (userId == UserHandle.USER_SYSTEM) {
+ output.credentialProtectedDataDir =
+ pkg.getBaseAppDataCredentialProtectedDirForSystemUser() + packageName;
+ output.deviceProtectedDataDir =
+ pkg.getBaseAppDataDeviceProtectedDirForSystemUser() + packageName;
+ } else {
+ // Convert /data/user/0/ -> /data/user/1/com.example.app
+ String userIdString = String.valueOf(userId);
+ int credentialLength = pkg.getBaseAppDataCredentialProtectedDirForSystemUser().length();
+ output.credentialProtectedDataDir =
+ new StringBuilder(pkg.getBaseAppDataCredentialProtectedDirForSystemUser())
+ .replace(credentialLength - 2, credentialLength - 1, userIdString)
+ .append(packageName)
+ .toString();
+ int deviceLength = pkg.getBaseAppDataDeviceProtectedDirForSystemUser().length();
+ output.deviceProtectedDataDir =
+ new StringBuilder(pkg.getBaseAppDataDeviceProtectedDirForSystemUser())
+ .replace(deviceLength - 2, deviceLength - 1, userIdString)
+ .append(packageName)
+ .toString();
+ }
+
+ if (input.isDefaultToDeviceProtectedStorage()
+ && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
+ output.dataDir = output.deviceProtectedDataDir;
+ } else {
+ output.dataDir = output.credentialProtectedDataDir;
+ }
+ }
+
/**
* Wraps {@link PackageInfoUtils#generateApplicationInfo} with a cache.
*/
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index 0e3e110..b4c6e9d 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -16,6 +16,7 @@
package com.android.server.pm.parsing.pkg;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.ActivityInfo;
@@ -28,7 +29,9 @@
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
import android.content.res.TypedArray;
+import android.os.Environment;
import android.os.Parcel;
+import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.text.TextUtils;
@@ -38,6 +41,7 @@
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
import com.android.server.pm.parsing.PackageInfoUtils;
+import java.io.File;
import java.util.UUID;
/**
@@ -87,8 +91,6 @@
@DataClass.ParcelWith(ForInternedString.class)
private final String manifestPackageName;
- private boolean stub;
-
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
protected String nativeLibraryDir;
@@ -97,8 +99,6 @@
@DataClass.ParcelWith(ForInternedString.class)
protected String nativeLibraryRootDir;
- private boolean nativeLibraryRootRequiresIsa;
-
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
protected String primaryCpuAbi;
@@ -116,44 +116,102 @@
@DataClass.ParcelWith(ForInternedString.class)
protected String seInfoUser;
- private boolean coreApp;
-
- private boolean system;
- private boolean factoryTest;
-
- private boolean systemExt;
- private boolean privileged;
- private boolean oem;
- private boolean vendor;
- private boolean product;
- private boolean odm;
-
- private boolean signedWithPlatformKey;
-
/**
* This is an appId, the uid if the userId is == USER_SYSTEM
*/
private int uid = -1;
+ // This is kept around as a boolean to avoid flag calculation
+ // during ApplicationInfo generation.
+ private boolean nativeLibraryRootRequiresIsa;
+
+ private int mBooleans;
+
+ /**
+ * @see ParsingPackageImpl.Booleans
+ */
+ private static class Booleans {
+ @IntDef({
+ CORE_APP,
+ SYSTEM,
+ FACTORY_TEST,
+ SYSTEM_EXT,
+ PRIVILEGED,
+ OEM,
+ VENDOR,
+ PRODUCT,
+ ODM,
+ SIGNED_WITH_PLATFORM_KEY,
+ NATIVE_LIBRARY_ROOT_REQUIRES_ISA,
+ STUB,
+ })
+ public @interface Flags {}
+
+ private static final int CORE_APP = 1;
+ private static final int SYSTEM = 1 << 1;
+ private static final int FACTORY_TEST = 1 << 2;
+ private static final int SYSTEM_EXT = 1 << 3;
+ private static final int PRIVILEGED = 1 << 4;
+ private static final int OEM = 1 << 5;
+ private static final int VENDOR = 1 << 6;
+ private static final int PRODUCT = 1 << 7;
+ private static final int ODM = 1 << 8;
+ private static final int SIGNED_WITH_PLATFORM_KEY = 1 << 9;
+ private static final int NATIVE_LIBRARY_ROOT_REQUIRES_ISA = 1 << 10;
+ private static final int STUB = 1 << 11;
+ }
+
+ private ParsedPackage setBoolean(@Booleans.Flags int flag, boolean value) {
+ if (value) {
+ mBooleans |= flag;
+ } else {
+ mBooleans &= ~flag;
+ }
+ return this;
+ }
+
+ private boolean getBoolean(@Booleans.Flags int flag) {
+ return (mBooleans & flag) != 0;
+ }
+
+ // Derived fields
+ private int mBaseAppInfoFlags;
+ private int mBaseAppInfoPrivateFlags;
+ private String mBaseAppDataCredentialProtectedDirForSystemUser;
+ private String mBaseAppDataDeviceProtectedDirForSystemUser;
+
@VisibleForTesting
public PackageImpl(@NonNull String packageName, @NonNull String baseApkPath,
@NonNull String path, @Nullable TypedArray manifestArray, boolean isCoreApp) {
super(packageName, baseApkPath, path, manifestArray);
this.manifestPackageName = this.packageName;
- this.coreApp = isCoreApp;
+ setBoolean(Booleans.CORE_APP, isCoreApp);
}
@Override
public ParsedPackage hideAsParsed() {
+ super.hideAsParsed();
return this;
}
@Override
public AndroidPackage hideAsFinal() {
// TODO(b/135203078): Lock as immutable
+ assignDerivedFields();
return this;
}
+ private void assignDerivedFields() {
+ mBaseAppInfoFlags = PackageInfoUtils.appInfoFlags(this, null);
+ mBaseAppInfoPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(this, null);
+ String baseAppDataDir = Environment.getDataDirectoryPath(getVolumeUuid()) + File.separator;
+ String systemUserSuffix = File.separator + UserHandle.USER_SYSTEM + File.separator;
+ mBaseAppDataCredentialProtectedDirForSystemUser = TextUtils.safeIntern(
+ baseAppDataDir + Environment.DIR_USER_CE + systemUserSuffix);
+ mBaseAppDataDeviceProtectedDirForSystemUser = TextUtils.safeIntern(
+ baseAppDataDir + Environment.DIR_USER_DE + systemUserSuffix);
+ }
+
@Override
public long getLongVersionCode() {
return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode);
@@ -437,8 +495,7 @@
@Override
public ParsedPackage setCoreApp(boolean coreApp) {
- this.coreApp = coreApp;
- return this;
+ return setBoolean(Booleans.CORE_APP, coreApp);
}
@Override
@@ -456,8 +513,8 @@
@Override
public ApplicationInfo toAppInfoWithoutState() {
ApplicationInfo appInfo = super.toAppInfoWithoutStateWithoutFlags();
- appInfo.flags = PackageInfoUtils.appInfoFlags(this, null);
- appInfo.privateFlags = PackageInfoUtils.appInfoPrivateFlags(this, null);
+ appInfo.flags = mBaseAppInfoFlags;
+ appInfo.privateFlags = mBaseAppInfoPrivateFlags;
appInfo.nativeLibraryDir = nativeLibraryDir;
appInfo.nativeLibraryRootDir = nativeLibraryRootDir;
appInfo.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa;
@@ -479,7 +536,6 @@
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
sForInternedString.parcel(this.manifestPackageName, dest, flags);
- dest.writeBoolean(this.stub);
dest.writeString(this.nativeLibraryDir);
dest.writeString(this.nativeLibraryRootDir);
dest.writeBoolean(this.nativeLibraryRootRequiresIsa);
@@ -489,22 +545,12 @@
dest.writeString(this.seInfo);
dest.writeString(this.seInfoUser);
dest.writeInt(this.uid);
- dest.writeBoolean(this.coreApp);
- dest.writeBoolean(this.system);
- dest.writeBoolean(this.factoryTest);
- dest.writeBoolean(this.systemExt);
- dest.writeBoolean(this.privileged);
- dest.writeBoolean(this.oem);
- dest.writeBoolean(this.vendor);
- dest.writeBoolean(this.product);
- dest.writeBoolean(this.odm);
- dest.writeBoolean(this.signedWithPlatformKey);
+ dest.writeInt(this.mBooleans);
}
public PackageImpl(Parcel in) {
super(in);
this.manifestPackageName = sForInternedString.unparcel(in);
- this.stub = in.readBoolean();
this.nativeLibraryDir = in.readString();
this.nativeLibraryRootDir = in.readString();
this.nativeLibraryRootRequiresIsa = in.readBoolean();
@@ -514,16 +560,9 @@
this.seInfo = in.readString();
this.seInfoUser = in.readString();
this.uid = in.readInt();
- this.coreApp = in.readBoolean();
- this.system = in.readBoolean();
- this.factoryTest = in.readBoolean();
- this.systemExt = in.readBoolean();
- this.privileged = in.readBoolean();
- this.oem = in.readBoolean();
- this.vendor = in.readBoolean();
- this.product = in.readBoolean();
- this.odm = in.readBoolean();
- this.signedWithPlatformKey = in.readBoolean();
+ this.mBooleans = in.readInt();
+
+ assignDerivedFields();
}
public static final Creator<PackageImpl> CREATOR = new Creator<PackageImpl>() {
@@ -544,9 +583,8 @@
return manifestPackageName;
}
- @DataClass.Generated.Member
public boolean isStub() {
- return stub;
+ return getBoolean(Booleans.STUB);
}
@Nullable
@@ -598,52 +636,52 @@
@Override
public boolean isCoreApp() {
- return coreApp;
+ return getBoolean(Booleans.CORE_APP);
}
@Override
public boolean isSystem() {
- return system;
+ return getBoolean(Booleans.SYSTEM);
}
@Override
public boolean isFactoryTest() {
- return factoryTest;
+ return getBoolean(Booleans.FACTORY_TEST);
}
@Override
public boolean isSystemExt() {
- return systemExt;
+ return getBoolean(Booleans.SYSTEM_EXT);
}
@Override
public boolean isPrivileged() {
- return privileged;
+ return getBoolean(Booleans.PRIVILEGED);
}
@Override
public boolean isOem() {
- return oem;
+ return getBoolean(Booleans.OEM);
}
@Override
public boolean isVendor() {
- return vendor;
+ return getBoolean(Booleans.VENDOR);
}
@Override
public boolean isProduct() {
- return product;
+ return getBoolean(Booleans.PRODUCT);
}
@Override
public boolean isOdm() {
- return odm;
+ return getBoolean(Booleans.ODM);
}
@Override
public boolean isSignedWithPlatformKey() {
- return signedWithPlatformKey;
+ return getBoolean(Booleans.SIGNED_WITH_PLATFORM_KEY);
}
/**
@@ -656,7 +694,7 @@
@DataClass.Generated.Member
public PackageImpl setStub(boolean value) {
- stub = value;
+ setBoolean(Booleans.STUB, value);
return this;
}
@@ -668,77 +706,72 @@
@Override
public PackageImpl setSystem(boolean value) {
- system = value;
+ setBoolean(Booleans.SYSTEM, value);
return this;
}
@Override
public PackageImpl setFactoryTest(boolean value) {
- factoryTest = value;
+ setBoolean(Booleans.FACTORY_TEST, value);
return this;
}
@Override
public PackageImpl setSystemExt(boolean value) {
- systemExt = value;
+ setBoolean(Booleans.SYSTEM_EXT, value);
return this;
}
@Override
public PackageImpl setPrivileged(boolean value) {
- privileged = value;
+ setBoolean(Booleans.PRIVILEGED, value);
return this;
}
@Override
public PackageImpl setOem(boolean value) {
- oem = value;
+ setBoolean(Booleans.OEM, value);
return this;
}
@Override
public PackageImpl setVendor(boolean value) {
- vendor = value;
+ setBoolean(Booleans.VENDOR, value);
return this;
}
@Override
public PackageImpl setProduct(boolean value) {
- product = value;
+ setBoolean(Booleans.PRODUCT, value);
return this;
}
@Override
public PackageImpl setOdm(boolean value) {
- odm = value;
+ setBoolean(Booleans.ODM, value);
return this;
}
@Override
public PackageImpl setSignedWithPlatformKey(boolean value) {
- signedWithPlatformKey = value;
+ setBoolean(Booleans.SIGNED_WITH_PLATFORM_KEY, value);
return this;
}
- /**
- * This is an appId, the uid if the userId is == USER_SYSTEM
- */
@Override
public PackageImpl setUid(int value) {
uid = value;
return this;
}
- @DataClass.Generated(
- time = 1580517688900L,
- codegenVersion = "1.0.14",
- sourceFile = "frameworks/base/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java",
- inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String manifestPackageName\nprivate boolean stub\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String nativeLibraryDir\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String nativeLibraryRootDir\nprivate boolean nativeLibraryRootRequiresIsa\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String primaryCpuAbi\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String secondaryCpuAbi\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String secondaryNativeLibraryDir\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String seInfo\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String seInfoUser\nprivate boolean system\nprivate boolean factoryTest\nprivate boolean systemExt\nprivate boolean privileged\nprivate boolean oem\nprivate boolean vendor\nprivate boolean product\nprivate boolean odm\nprivate boolean signedWithPlatformKey\nprivate int uid\npublic static final com.android.server.pm.parsing.pkg.Creator<com.android.server.pm.parsing.pkg.PackageImpl> CREATOR\npublic static com.android.server.pm.parsing.pkg.PackageImpl forParsing(java.lang.String,java.lang.String,java.lang.String,android.content.res.TypedArray,boolean)\npublic static com.android.server.pm.parsing.pkg.AndroidPackage buildFakeForDeletion(java.lang.String,java.lang.String)\npublic static @com.android.internal.annotations.VisibleForTesting android.content.pm.parsing.ParsingPackage forTesting(java.lang.String)\npublic static @com.android.internal.annotations.VisibleForTesting android.content.pm.parsing.ParsingPackage forTesting(java.lang.String,java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.ParsedPackage hideAsParsed()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.AndroidPackage hideAsFinal()\npublic @java.lang.Override long getLongVersionCode()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl removePermission(int)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl addUsesOptionalLibrary(int,java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl addUsesLibrary(int,java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl removeUsesLibrary(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl removeUsesOptionalLibrary(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSigningDetails(android.content.pm.PackageParser.SigningDetails)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setRestrictUpdateHash(byte)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setRealPackage(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setPersistent(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setDefaultToDeviceProtectedStorage(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setDirectBootAware(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl clearProtectedBroadcasts()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl clearOriginalPackages()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl clearAdoptPermissions()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setCodePath(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setPackageName(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setAllComponentsDirectBootAware(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setBaseCodePath(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setNativeLibraryDir(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setNativeLibraryRootDir(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setPrimaryCpuAbi(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSecondaryCpuAbi(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSecondaryNativeLibraryDir(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSeInfo(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSeInfoUser(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSplitCodePaths(java.lang.String[])\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl capPermissionPriorities()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl markNotActivitiesAsNotExportedIfSingleUser()\npublic @java.lang.Override java.util.UUID getStorageUuid()\npublic @java.lang.Deprecated @java.lang.Override java.lang.String toAppInfoToString()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.ParsedPackage setCoreApp(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.ParsedPackage setVersionCode(int)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.ParsedPackage setVersionCodeMajor(int)\npublic @java.lang.Override android.content.pm.ApplicationInfo toAppInfoWithoutState()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass PackageImpl extends android.content.pm.parsing.ParsingPackageImpl implements [com.android.server.pm.parsing.pkg.ParsedPackage, com.android.server.pm.parsing.pkg.AndroidPackage]\n@com.android.internal.util.DataClass(genConstructor=false, genParcelable=false, genAidl=false, genBuilder=false, genHiddenConstructor=false, genCopyConstructor=false)")
- @Deprecated
- private void __metadata() {}
+ // The following methods are explicitly not inside any interface. These are hidden under
+ // PackageImpl which is only accessible to the system server. This is to prevent/discourage
+ // usage of these fields outside of the utility classes.
+ public String getBaseAppDataCredentialProtectedDirForSystemUser() {
+ return mBaseAppDataCredentialProtectedDirForSystemUser;
+ }
-
- //@formatter:on
- // End of generated code
-
+ public String getBaseAppDataDeviceProtectedDirForSystemUser() {
+ return mBaseAppDataDeviceProtectedDirForSystemUser;
+ }
}
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
index 2660f2b..657f32c 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
@@ -105,6 +105,9 @@
ParsedPackage setSecondaryNativeLibraryDir(String secondaryNativeLibraryDir);
+ /**
+ * This is an appId, the uid if the userId is == USER_SYSTEM
+ */
ParsedPackage setUid(int uid);
ParsedPackage setVersionCode(int versionCode);
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index f7721a4..1ae430a 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1268,10 +1268,10 @@
continue;
}
- // Preserve whitelisting flags.
+ // Preserve allowlisting flags.
newFlags |= (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT);
- // If we are whitelisting the permission, update the exempt flag before grant.
+ // If we are allowlisting the permission, update the exempt flag before grant.
if (whitelistRestrictedPermissions && pm.isPermissionRestricted(permission)) {
pm.updatePermissionFlags(permission, pkg,
PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT,
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 75e5944..2f9e199 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -53,7 +53,7 @@
import static com.android.server.pm.PackageManagerService.DEBUG_PERMISSIONS;
import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
+import static com.android.server.pm.permission.UidPermissionState.PERMISSION_OPERATION_FAILURE;
import static java.util.concurrent.TimeUnit.SECONDS;
@@ -1526,7 +1526,7 @@
return;
}
- case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
+ case UidPermissionState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
if (callback != null) {
callback.onGidsChanged(UserHandle.getAppId(pkg.getUid()), userId);
}
@@ -2647,6 +2647,7 @@
uidState.setGlobalGids(mGlobalGids);
ArraySet<String> newImplicitPermissions = new ArraySet<>();
+ final String friendlyName = pkg.getPackageName() + "(" + pkg.getUid() + ")";
final int N = pkg.getRequestedPermissions().size();
for (int i = 0; i < N; i++) {
@@ -2657,7 +2658,7 @@
String upgradedActivityRecognitionPermission = null;
if (DEBUG_INSTALL && bp != null) {
- Log.i(TAG, "Package " + pkg.getPackageName()
+ Log.i(TAG, "Package " + friendlyName
+ " checking " + permName + ": " + bp);
}
@@ -2666,7 +2667,7 @@
pkg.getPackageName())) {
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, "Unknown permission " + permName
- + " in package " + pkg.getPackageName());
+ + " in package " + friendlyName);
}
}
continue;
@@ -2682,7 +2683,7 @@
newImplicitPermissions.add(permName);
if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, permName + " is newly added for " + pkg.getPackageName());
+ Slog.i(TAG, permName + " is newly added for " + friendlyName);
}
} else {
// Special case for Activity Recognition permission. Even if AR permission
@@ -2705,7 +2706,7 @@
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, permName + " is newly added for "
- + pkg.getPackageName());
+ + friendlyName);
}
break;
}
@@ -2727,7 +2728,7 @@
if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
if (DEBUG_PERMISSIONS) {
Log.i(TAG, "Denying runtime-only permission " + bp.getName()
- + " for package " + pkg.getPackageName());
+ + " for package " + friendlyName);
}
continue;
}
@@ -3049,7 +3050,7 @@
|| packageOfInterest.equals(pkg.getPackageName())) {
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, "Not granting permission " + perm
- + " to package " + pkg.getPackageName()
+ + " to package " + friendlyName
+ " because it was previously installed without");
}
}
@@ -3063,7 +3064,7 @@
changedInstallPermission = true;
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, "Un-granting permission " + perm
- + " from package " + pkg.getPackageName()
+ + " from package " + friendlyName
+ " (protectionLevel=" + bp.getProtectionLevel()
+ " flags=0x"
+ Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg,
@@ -3077,7 +3078,7 @@
&& (packageOfInterest == null
|| packageOfInterest.equals(pkg.getPackageName()))) {
Slog.i(TAG, "Not granting permission " + perm
- + " to package " + pkg.getPackageName()
+ + " to package " + friendlyName
+ " (protectionLevel=" + bp.getProtectionLevel()
+ " flags=0x"
+ Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg,
@@ -4025,7 +4026,7 @@
// TODO(zhanghai): This doesn't make sense. revokePermission() doesn't fail, and why are
// we only killing the uid when gids changed, instead of any permission change?
if (uidState.revokePermission(bp)
- == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
+ == UidPermissionState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
affectedUserId = userId;
}
}
@@ -5243,10 +5244,10 @@
@Override
public void onNewUserCreated(int userId) {
- mDefaultPermissionGrantPolicy.grantDefaultPermissions(userId);
// NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG
PermissionManagerService.this.updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL,
true, mDefaultPermissionCallback);
+ mDefaultPermissionGrantPolicy.grantDefaultPermissions(userId);
}
@Override
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 7f6a1d4..6d9bd2a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -189,7 +189,7 @@
@NonNull AndroidPackage pkg, @NonNull int[] userIds,
@NonNull List<String> permissions, int callingUid,
@PackageManager.PermissionWhitelistFlags int whitelistFlags);
- /** Sets the whitelisted, restricted permissions for the given package. */
+ /** Sets the allowlisted, restricted permissions for the given package. */
public abstract void setWhitelistedRestrictedPermissions(
@NonNull String packageName, @NonNull List<String> permissions,
@PackageManager.PermissionWhitelistFlags int flags, int userId);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d01a30f..35b1449 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -281,7 +281,8 @@
static final int LONG_PRESS_HOME_NOTHING = 0;
static final int LONG_PRESS_HOME_ALL_APPS = 1;
static final int LONG_PRESS_HOME_ASSIST = 2;
- static final int LAST_LONG_PRESS_HOME_BEHAVIOR = LONG_PRESS_HOME_ASSIST;
+ static final int LONG_PRESS_HOME_NOTIFICATION_PANEL = 3;
+ static final int LAST_LONG_PRESS_HOME_BEHAVIOR = LONG_PRESS_HOME_NOTIFICATION_PANEL;
// must match: config_doubleTapOnHomeBehavior in config.xml
static final int DOUBLE_TAP_HOME_NOTHING = 0;
@@ -1694,8 +1695,18 @@
case LONG_PRESS_HOME_ASSIST:
launchAssistAction(null, deviceId);
break;
+ case LONG_PRESS_HOME_NOTIFICATION_PANEL:
+ IStatusBarService statusBarService = getStatusBarService();
+ if (statusBarService != null) {
+ try {
+ statusBarService.togglePanel();
+ } catch (RemoteException e) {
+ // do nothing.
+ }
+ }
+ break;
default:
- Log.w(TAG, "Undefined home long press behavior: "
+ Log.w(TAG, "Undefined long press on home behavior: "
+ mLongPressOnHomeBehavior);
break;
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 9ff164a..6044ee1 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3494,7 +3494,7 @@
}
if (mDeviceIdleMode) {
// If we are in idle mode, we will also ignore all partial wake locks that are
- // for application uids that are not whitelisted.
+ // for application uids that are not allowlisted.
final UidState state = wakeLock.mUidState;
if (Arrays.binarySearch(mDeviceIdleWhitelist, appid) < 0 &&
Arrays.binarySearch(mDeviceIdleTempWhitelist, appid) < 0 &&
diff --git a/services/core/java/com/android/server/power/WirelessChargerDetector.java b/services/core/java/com/android/server/power/WirelessChargerDetector.java
index 0d910e4..f4a014a 100644
--- a/services/core/java/com/android/server/power/WirelessChargerDetector.java
+++ b/services/core/java/com/android/server/power/WirelessChargerDetector.java
@@ -92,7 +92,7 @@
// cosine of the maximum angle variance that we tolerate while at rest.
private static final double MOVEMENT_ANGLE_COS_THRESHOLD = Math.cos(5 * Math.PI / 180);
- // Sanity thresholds for the gravity vector.
+ // Validity thresholds for the gravity vector.
private static final double MIN_GRAVITY = SensorManager.GRAVITY_EARTH - 1.0f;
private static final double MAX_GRAVITY = SensorManager.GRAVITY_EARTH + 1.0f;
diff --git a/services/core/java/com/android/server/powerstats/BatteryTrigger.java b/services/core/java/com/android/server/powerstats/BatteryTrigger.java
new file mode 100644
index 0000000..c1f97f2
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/BatteryTrigger.java
@@ -0,0 +1,65 @@
+/*
+ * 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.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.util.Log;
+
+/**
+ * BatteryTrigger instantiates a BroadcastReceiver that listens for changes
+ * to the battery. When the battery level drops by 1% a message is sent to
+ * the PowerStatsLogger to log the rail energy data to on-device storage.
+ */
+public final class BatteryTrigger extends PowerStatsLogTrigger {
+ private static final String TAG = BatteryTrigger.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private int mBatteryLevel = 0;
+
+ private final BroadcastReceiver mBatteryLevelReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case Intent.ACTION_BATTERY_CHANGED:
+ int newBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
+
+ if (newBatteryLevel < mBatteryLevel) {
+ if (DEBUG) Log.d(TAG, "Battery level dropped. Log rail data");
+ logPowerStatsData();
+ }
+
+ mBatteryLevel = newBatteryLevel;
+ break;
+ }
+ }
+ };
+
+ public BatteryTrigger(Context context, PowerStatsLogger powerStatsLogger,
+ boolean triggerEnabled) {
+ super(context, powerStatsLogger);
+
+ if (triggerEnabled) {
+ IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ Intent batteryStatus = mContext.registerReceiver(mBatteryLevelReceiver, filter);
+ mBatteryLevel = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsData.java b/services/core/java/com/android/server/powerstats/PowerStatsData.java
new file mode 100644
index 0000000..755bd5f
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/PowerStatsData.java
@@ -0,0 +1,286 @@
+/*
+ * 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
new file mode 100644
index 0000000..84a6fc9
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
@@ -0,0 +1,223 @@
+/*
+ * 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.content.Context;
+import android.util.Log;
+
+import com.android.internal.util.FileRotator;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * PowerStatsDataStorage implements the on-device storage cache for energy
+ * data. This data must be persisted across boot cycles so we store it
+ * on-device. Versioning of this data is handled by deleting any data that
+ * does not match the current version. The cache is implemented as a circular
+ * buffer using the FileRotator class in android.util. We maintain 48 hours
+ * worth of logs in 12 files (4 hours each).
+ */
+public class PowerStatsDataStorage {
+ private static final String TAG = PowerStatsDataStorage.class.getSimpleName();
+
+ private static final long MILLISECONDS_PER_HOUR = 1000 * 60 * 60;
+ // Rotate files every 4 hours.
+ private static final long ROTATE_AGE_MILLIS = 4 * MILLISECONDS_PER_HOUR;
+ // Store 48 hours worth of data.
+ private static final long DELETE_AGE_MILLIS = 48 * MILLISECONDS_PER_HOUR;
+
+ private final ReentrantLock mLock = new ReentrantLock();
+ private File mDataStorageDir;
+ private final FileRotator mFileRotator;
+
+ private static class DataElement {
+ private static final int LENGTH_FIELD_WIDTH = 4;
+ private static final int MAX_DATA_ELEMENT_SIZE = 1000;
+
+ private byte[] mData;
+
+ private byte[] toByteArray() throws IOException {
+ ByteArrayOutputStream data = new ByteArrayOutputStream();
+ data.write(ByteBuffer.allocate(LENGTH_FIELD_WIDTH).putInt(mData.length).array());
+ data.write(mData);
+ return data.toByteArray();
+ }
+
+ protected byte[] getData() {
+ return mData;
+ }
+
+ private DataElement(byte[] data) {
+ mData = data;
+ }
+
+ private DataElement(InputStream in) throws IOException {
+ byte[] lengthBytes = new byte[LENGTH_FIELD_WIDTH];
+ int bytesRead = in.read(lengthBytes);
+ mData = new byte[0];
+
+ if (bytesRead == LENGTH_FIELD_WIDTH) {
+ int length = ByteBuffer.wrap(lengthBytes).getInt();
+
+ if (0 < length && length < MAX_DATA_ELEMENT_SIZE) {
+ mData = new byte[length];
+ bytesRead = in.read(mData);
+
+ if (bytesRead != length) {
+ throw new IOException("Invalid bytes read, expected: " + length
+ + ", actual: " + bytesRead);
+ }
+ } else {
+ throw new IOException("DataElement size is invalid: " + length);
+ }
+ } else {
+ throw new IOException("Did not read " + LENGTH_FIELD_WIDTH + " bytes (" + bytesRead
+ + ")");
+ }
+ }
+ }
+
+ /**
+ * Used by external classes to read DataElements from on-device storage.
+ * This callback is passed in to the read() function and is called for
+ * each DataElement read from on-device storage.
+ */
+ public interface DataElementReadCallback {
+ /**
+ * When performing a read of the on-device storage this callback
+ * must be passed in to the read function. The function will be
+ * called for each DataElement read from on-device storage.
+ *
+ * @param data Byte array containing a DataElement payload.
+ */
+ void onReadDataElement(byte[] data);
+ }
+
+ private static class DataReader implements FileRotator.Reader {
+ private DataElementReadCallback mCallback;
+
+ DataReader(DataElementReadCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void read(InputStream in) throws IOException {
+ while (in.available() > 0) {
+ try {
+ DataElement dataElement = new DataElement(in);
+ mCallback.onReadDataElement(dataElement.getData());
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to read from storage. " + e.getMessage());
+ }
+ }
+ }
+ }
+
+ private static class DataRewriter implements FileRotator.Rewriter {
+ byte[] mActiveFileData;
+ byte[] mNewData;
+
+ DataRewriter(byte[] data) {
+ mActiveFileData = new byte[0];
+ mNewData = data;
+ }
+
+ @Override
+ public void reset() {
+ // ignored
+ }
+
+ @Override
+ public void read(InputStream in) throws IOException {
+ mActiveFileData = new byte[in.available()];
+ in.read(mActiveFileData);
+ }
+
+ @Override
+ public boolean shouldWrite() {
+ return true;
+ }
+
+ @Override
+ public void write(OutputStream out) throws IOException {
+ out.write(mActiveFileData);
+ out.write(mNewData);
+ }
+ }
+
+ public PowerStatsDataStorage(Context context, File dataStoragePath,
+ String dataStorageFilename) {
+ mDataStorageDir = dataStoragePath;
+
+ if (!mDataStorageDir.exists() && !mDataStorageDir.mkdirs()) {
+ Log.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
+ // filename, so any files that don't match the current version number can be deleted.
+ File[] files = mDataStorageDir.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ if (!files[i].getName().matches(dataStorageFilename + "(.*)")) {
+ files[i].delete();
+ }
+ }
+
+ mFileRotator = new FileRotator(mDataStorageDir,
+ dataStorageFilename,
+ ROTATE_AGE_MILLIS,
+ DELETE_AGE_MILLIS);
+ }
+ }
+
+ /**
+ * Writes data stored in PowerStatsDataStorage to a file descriptor.
+ *
+ * @param data Byte array to write to on-device storage. Byte array is
+ * converted to a DataElement which prefixes the payload with
+ * the data length. The DataElement is then converted to a byte
+ * array and written to on-device storage.
+ */
+ public void write(byte[] data) {
+ 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);
+ }
+
+ mLock.unlock();
+ }
+
+ /**
+ * Reads all DataElements stored in on-device storage. For each
+ * DataElement retrieved from on-device storage, callback is called.
+ */
+ public void read(DataElementReadCallback callback) throws IOException {
+ mFileRotator.readMatching(new DataReader(callback), Long.MIN_VALUE, Long.MAX_VALUE);
+ }
+}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
new file mode 100644
index 0000000..dc996a3
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
@@ -0,0 +1,105 @@
+/*
+ * 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;
+
+/**
+ * PowerStatsHALWrapper is a wrapper class for the PowerStats HAL API calls.
+ */
+public final class PowerStatsHALWrapper {
+ private static final String TAG = PowerStatsHALWrapper.class.getSimpleName();
+
+ /**
+ * IPowerStatsHALWrapper defines the interface to the PowerStatsHAL.
+ */
+ public interface IPowerStatsHALWrapper {
+ /**
+ * Returns rail info for all available ODPM rails.
+ *
+ * @return array of RailInfo objects containing rail info for all
+ * available rails.
+ */
+ PowerStatsData.RailInfo[] readRailInfo();
+
+ /**
+ * 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.
+ */
+ PowerStatsData.EnergyData[] readEnergyData();
+
+ /**
+ * 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
+ */
+ boolean initialize();
+ }
+
+ /**
+ * PowerStatsHALWrapperImpl is the implementation of the IPowerStatsHALWrapper
+ * used by the PowerStatsService. Other implementations will be used by the testing
+ * 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();
+
+ /**
+ * 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();
+ }
+
+ /**
+ * 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();
+ }
+
+ /**
+ * 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 boolean initialize() {
+ return nativeInit();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogTrigger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogTrigger.java
new file mode 100644
index 0000000..1754185
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogTrigger.java
@@ -0,0 +1,44 @@
+/*
+ * 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.content.Context;
+import android.os.Message;
+
+/**
+ * PowerStatsLogTrigger is the base class for other trigger classes.
+ * It provides the logPowerStatsData() function which sends a message
+ * to the PowerStatsLogger to read the rail energy data and log it to
+ * on-device storage. This class is abstract and cannot be instantiated.
+ */
+public abstract class PowerStatsLogTrigger {
+ private static final String TAG = PowerStatsLogTrigger.class.getSimpleName();
+
+ protected Context mContext;
+ private PowerStatsLogger mPowerStatsLogger;
+
+ protected void logPowerStatsData() {
+ Message.obtain(
+ mPowerStatsLogger,
+ PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE).sendToTarget();
+ }
+
+ public PowerStatsLogTrigger(Context context, PowerStatsLogger powerStatsLogger) {
+ mContext = context;
+ mPowerStatsLogger = powerStatsLogger;
+ }
+}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
new file mode 100644
index 0000000..71a34a4
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -0,0 +1,109 @@
+/*
+ * 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.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+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.
+ */
+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 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());
+ break;
+ }
+ }
+
+ /**
+ * Writes 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.
+ */
+ public void writeToFile(FileDescriptor fd) {
+ if (DEBUG) Log.d(TAG, "Writing to file");
+
+ final ProtoOutputStream pos = new ProtoOutputStream(fd);
+
+ try {
+ PowerStatsData railInfo = new PowerStatsData(mPowerStatsHALWrapper.readRailInfo());
+ railInfo.toProto(pos);
+ if (DEBUG) railInfo.print();
+
+ mPowerStatsDataStorage.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.
+ final PowerStatsData energyData = new PowerStatsData(pis);
+ energyData.toProto(pos);
+ if (DEBUG) energyData.print();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to write energy data to incident report.");
+ }
+ }
+ });
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to write rail info to incident report.");
+ }
+
+ pos.flush();
+ }
+
+ public PowerStatsLogger(Context context, File dataStoragePath, String dataStorageFilename,
+ IPowerStatsHALWrapper powerStatsHALWrapper) {
+ super(Looper.getMainLooper());
+ mPowerStatsHALWrapper = powerStatsHALWrapper;
+ mPowerStatsDataStorage = new PowerStatsDataStorage(context, dataStoragePath,
+ dataStorageFilename);
+ }
+}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
new file mode 100644
index 0000000..fd609c1
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -0,0 +1,135 @@
+/*
+ * 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.content.Context;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.UserHandle;
+import android.util.Log;
+
+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 java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * This class provides a system service that estimates system power usage
+ * per subsystem (modem, wifi, gps, display, etc) and provides those power
+ * estimates to subscribers.
+ */
+public class PowerStatsService extends SystemService {
+ private static final String TAG = PowerStatsService.class.getSimpleName();
+ 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 final Injector mInjector;
+
+ private Context mContext;
+ private IPowerStatsHALWrapper mPowerStatsHALWrapper;
+ private PowerStatsLogger mPowerStatsLogger;
+ private BatteryTrigger mBatteryTrigger;
+ private TimerTrigger mTimerTrigger;
+
+ @VisibleForTesting
+ static class Injector {
+ File createDataStoragePath() {
+ return new File(Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM),
+ DATA_STORAGE_SUBDIR);
+ }
+
+ String createDataStorageFilename() {
+ return DATA_STORAGE_FILENAME;
+ }
+
+ IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() {
+ return new PowerStatsHALWrapperImpl();
+ }
+
+ PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
+ String dataStorageFilename, IPowerStatsHALWrapper powerStatsHALWrapper) {
+ return new PowerStatsLogger(context, dataStoragePath, dataStorageFilename,
+ powerStatsHALWrapper);
+ }
+
+ BatteryTrigger createBatteryTrigger(Context context, PowerStatsLogger powerStatsLogger) {
+ return new BatteryTrigger(context, powerStatsLogger, true /* trigger enabled */);
+ }
+
+ TimerTrigger createTimerTrigger(Context context, PowerStatsLogger powerStatsLogger) {
+ return new TimerTrigger(context, powerStatsLogger, true /* trigger enabled */);
+ }
+ }
+
+ private final class BinderService extends Binder {
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+
+ if (args.length > 0 && "--proto".equals(args[0])) {
+ mPowerStatsLogger.writeToFile(fd);
+ }
+ }
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_BOOT_COMPLETED) {
+ onSystemServiceReady();
+ }
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.POWER_STATS_SERVICE, new BinderService());
+ }
+
+ private void onSystemServiceReady() {
+ mPowerStatsHALWrapper = mInjector.createPowerStatsHALWrapperImpl();
+
+ if (mPowerStatsHALWrapper.initialize()) {
+ if (DEBUG) Log.d(TAG, "Starting PowerStatsService");
+
+ // Only start logger and triggers if initialization is successful.
+ mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext,
+ mInjector.createDataStoragePath(), mInjector.createDataStorageFilename(),
+ mPowerStatsHALWrapper);
+ mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger);
+ mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger);
+ } else {
+ Log.e(TAG, "Initialization of PowerStatsHAL wrapper failed");
+ }
+ }
+
+ public PowerStatsService(Context context) {
+ this(context, new Injector());
+ }
+
+ @VisibleForTesting
+ public PowerStatsService(Context context, Injector injector) {
+ super(context);
+ mContext = context;
+ mInjector = injector;
+ }
+}
diff --git a/services/core/java/com/android/server/powerstats/TEST_MAPPING b/services/core/java/com/android/server/powerstats/TEST_MAPPING
new file mode 100644
index 0000000..79224a5
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.powerstats"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/powerstats/TimerTrigger.java b/services/core/java/com/android/server/powerstats/TimerTrigger.java
new file mode 100644
index 0000000..a9bee8b
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/TimerTrigger.java
@@ -0,0 +1,56 @@
+/*
+ * 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.content.Context;
+import android.os.Handler;
+import android.util.Log;
+
+/**
+ * TimerTrigger sets a 60 second opportunistic timer using postDelayed.
+ * When the timer expires a message is sent to the PowerStatsLogger to
+ * read the rail energy data and log it to on-device storage.
+ */
+public final class TimerTrigger extends PowerStatsLogTrigger {
+ private static final String TAG = TimerTrigger.class.getSimpleName();
+ private static final boolean DEBUG = false;
+ // TODO(b/166689029): Make configurable through global settings.
+ private static final long LOG_PERIOD_MS = 60 * 1000;
+
+ private final Handler mHandler;
+
+ private Runnable mLogData = new Runnable() {
+ @Override
+ public void run() {
+ // 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");
+ logPowerStatsData();
+ }
+ };
+
+ public TimerTrigger(Context context, PowerStatsLogger powerStatsLogger,
+ boolean triggerEnabled) {
+ super(context, powerStatsLogger);
+ mHandler = mContext.getMainThreadHandler();
+
+ if (triggerEnabled) {
+ mLogData.run();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 6ba675d..9350edf 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -1099,7 +1099,7 @@
*/
@AnyThread
private boolean isRollbackWhitelisted(String packageName) {
- // TODO: Remove #isModule when the white list is ready.
+ // TODO: Remove #isModule when the allowlist is ready.
return SystemConfig.getInstance().getRollbackWhitelistedPackages().contains(packageName)
|| isModule(packageName);
}
diff --git a/services/core/java/com/android/server/search/Searchables.java b/services/core/java/com/android/server/search/Searchables.java
index c769a50..ca7f036 100644
--- a/services/core/java/com/android/server/search/Searchables.java
+++ b/services/core/java/com/android/server/search/Searchables.java
@@ -419,7 +419,7 @@
if (activities != null && !activities.isEmpty()) {
ActivityInfo ai = activities.get(0).activityInfo;
- // TODO: do some sanity checks here?
+ // TODO: do some validity checks here?
return new ComponentName(ai.packageName, ai.name);
}
Log.w(LOG_TAG, "No web search activity found");
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 0314cf8..c871a5e 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -2144,7 +2144,7 @@
JSONArray app_sizes = json.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY);
JSONArray app_data_sizes = json.getJSONArray(DiskStatsFileLogger.APP_DATA_KEY);
JSONArray app_cache_sizes = json.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY);
- // Sanity check: Ensure all 4 lists have the same length.
+ // Validity check: Ensure all 4 lists have the same length.
int length = pkg_names.length();
if (app_sizes.length() != length || app_data_sizes.length() != length
|| app_cache_sizes.length() != length) {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index b902b5a..e9215f9 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -20,6 +20,7 @@
import android.app.ITransientNotificationCallback;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowInsetsController.Appearance;
@@ -143,4 +144,9 @@
* request)
*/
void requestWindowMagnificationConnection(boolean request);
+
+ /**
+ * Handles a logging command from the WM shell command.
+ */
+ void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd);
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 9bc8afa..874b910 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -36,6 +36,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -534,6 +535,15 @@
} catch (RemoteException ex) { }
}
}
+
+ @Override
+ public void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd) {
+ if (mBar != null) {
+ try {
+ mBar.handleWindowManagerLoggingCommand(args, outFd);
+ } catch (RemoteException ex) { }
+ }
+ }
};
private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() {
diff --git a/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java b/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java
index 56bcdd9..e02cef1 100644
--- a/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java
+++ b/services/core/java/com/android/server/textservices/TextServicesManagerInternal.java
@@ -52,7 +52,7 @@
};
/**
- * @return Global instance if exists. Otherwise, a dummy no-op instance.
+ * @return Global instance if exists. Otherwise, a placeholder no-op instance.
*/
@NonNull
public static TextServicesManagerInternal get() {
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 7501d9f..73322a6 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -57,8 +57,12 @@
private static final String TAG = "TimeZoneDetectorService";
- /** A compile time switch for enabling / disabling geolocation-based time zone detection. */
- private static final boolean GEOLOCATION_TIME_ZONE_DETECTION_ENABLED = false;
+ /**
+ * A compile time constant "feature switch" for enabling / disabling location-based time zone
+ * detection on Android. If this is {@code false}, there should be few / little changes in
+ * behavior with previous releases and little overhead associated with geolocation components.
+ */
+ public static final boolean GEOLOCATION_TIME_ZONE_DETECTION_ENABLED = false;
/**
* Handles the service lifecycle for {@link TimeZoneDetectorService} and
diff --git a/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java b/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java
index 6e180bc..68c8eaa 100644
--- a/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java
+++ b/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java
@@ -173,7 +173,7 @@
return false;
}
- // Check if package name is white-listed here.
+ // Check if package name is allowlisted here.
if (!mUnbundledServicePackages.contains(serviceInfo.packageName)) {
Slog.w(TAG, "Ignoring atv remote provider service because the package has not "
+ "been set and/or whitelisted: "
diff --git a/services/core/java/com/android/server/tv/TvRemoteService.java b/services/core/java/com/android/server/tv/TvRemoteService.java
index 5894645..c71cdef62 100644
--- a/services/core/java/com/android/server/tv/TvRemoteService.java
+++ b/services/core/java/com/android/server/tv/TvRemoteService.java
@@ -24,10 +24,10 @@
/**
* TvRemoteService represents a system service that allows a connected
- * remote control (emote) service to inject white-listed input events
+ * remote control (emote) service to inject allowlisted input events
* and call other specified methods for functioning as an emote service.
* <p/>
- * This service is intended for use only by white-listed packages.
+ * This service is intended for use only by allowlisted packages.
*/
public class TvRemoteService extends SystemService implements Watchdog.Monitor {
private static final String TAG = "TvRemoteService";
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 53d5146..39f6c59 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -684,7 +684,7 @@
final int modeFlags = readIntAttribute(in, ATTR_MODE_FLAGS);
final long createdTime = readLongAttribute(in, ATTR_CREATED_TIME, now);
- // Sanity check that provider still belongs to source package
+ // Validity check that provider still belongs to source package
// Both direct boot aware and unaware packages are fine as we
// will do filtering at query time to avoid multiple parsing.
final ProviderInfo pi = getProviderInfo(uri.getAuthority(), sourceUserId,
diff --git a/services/core/java/com/android/server/utils/quota/QuotaTracker.java b/services/core/java/com/android/server/utils/quota/QuotaTracker.java
index 7a17c64..c1dfcf7 100644
--- a/services/core/java/com/android/server/utils/quota/QuotaTracker.java
+++ b/services/core/java/com/android/server/utils/quota/QuotaTracker.java
@@ -95,7 +95,7 @@
/** "Free quota status" for apps. */
@GuardedBy("mLock")
- private final SparseArrayMap<Boolean> mFreeQuota = new SparseArrayMap<>();
+ private final SparseArrayMap<String, Boolean> mFreeQuota = new SparseArrayMap<>();
private final AlarmManager mAlarmManager;
protected final Context mContext;
diff --git a/services/core/java/com/android/server/utils/quota/UptcMap.java b/services/core/java/com/android/server/utils/quota/UptcMap.java
index a3d6ee5..77a2a99 100644
--- a/services/core/java/com/android/server/utils/quota/UptcMap.java
+++ b/services/core/java/com/android/server/utils/quota/UptcMap.java
@@ -31,7 +31,7 @@
* @see Uptc
*/
class UptcMap<T> {
- private final SparseArrayMap<ArrayMap<String, T>> mData = new SparseArrayMap<>();
+ private final SparseArrayMap<String, ArrayMap<String, T>> mData = new SparseArrayMap<>();
public void add(int userId, @NonNull String packageName, @Nullable String tag,
@Nullable T obj) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 202a3dc..5f4d46c 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2022,7 +2022,7 @@
}
WallpaperData wd = mWallpaperMap.get(user.id);
if (wd == null) {
- // User hasn't started yet, so load her settings to peek at the wallpaper
+ // User hasn't started yet, so load their settings to peek at the wallpaper
loadSettingsLocked(user.id, false);
wd = mWallpaperMap.get(user.id);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index ad28124..3e63850 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -133,7 +133,7 @@
import static com.android.server.wm.ActivityRecordProto.NAME;
import static com.android.server.wm.ActivityRecordProto.NUM_DRAWN_WINDOWS;
import static com.android.server.wm.ActivityRecordProto.NUM_INTERESTING_WINDOWS;
-import static com.android.server.wm.ActivityRecordProto.PIP_AUTO_ENTER_ALLOWED;
+import static com.android.server.wm.ActivityRecordProto.PIP_AUTO_ENTER_ENABLED;
import static com.android.server.wm.ActivityRecordProto.PROC_ID;
import static com.android.server.wm.ActivityRecordProto.REPORTED_DRAWN;
import static com.android.server.wm.ActivityRecordProto.REPORTED_VISIBLE;
@@ -4754,7 +4754,7 @@
// returns. Just need to confirm this reasoning makes sense.
final boolean deferHidingClient = canEnterPictureInPicture
&& !isState(STARTED, STOPPING, STOPPED, PAUSED);
- if (deferHidingClient && pictureInPictureArgs.isAutoEnterAllowed()) {
+ if (deferHidingClient && pictureInPictureArgs.isAutoEnterEnabled()) {
// Go ahead and just put the activity in pip if it supports auto-pip.
mAtmService.enterPictureInPictureMode(this, pictureInPictureArgs);
return;
@@ -7693,7 +7693,7 @@
if (hasProcess()) {
proto.write(PROC_ID, app.getPid());
}
- proto.write(PIP_AUTO_ENTER_ALLOWED, pictureInPictureArgs.isAutoEnterAllowed());
+ proto.write(PIP_AUTO_ENTER_ENABLED, pictureInPictureArgs.isAutoEnterEnabled());
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 2657eb2..ebfd0cd 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4865,7 +4865,7 @@
"Requested PIP on an activity that doesn't support it");
}
- if (activity.pictureInPictureArgs.isAutoEnterAllowed()) {
+ if (activity.pictureInPictureArgs.isAutoEnterEnabled()) {
enterPictureInPictureMode(activity, activity.pictureInPictureArgs);
return;
}
diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java
index 8568d5f..4a90bbc 100644
--- a/services/core/java/com/android/server/wm/BarController.java
+++ b/services/core/java/com/android/server/wm/BarController.java
@@ -16,88 +16,19 @@
package com.android.server.wm;
-import static com.android.server.wm.BarControllerProto.STATE;
-import static com.android.server.wm.BarControllerProto.TRANSIENT_STATE;
-
import android.annotation.NonNull;
-import android.app.StatusBarManager;
import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-import android.view.View;
-import android.view.ViewRootImpl;
-import android.view.WindowManager;
-
-import com.android.server.LocalServices;
-import com.android.server.UiThread;
-import com.android.server.statusbar.StatusBarManagerInternal;
-
-import java.io.PrintWriter;
/**
* Controls state/behavior specific to a system bar window.
*/
public class BarController {
- private static final boolean DEBUG = false;
-
- private static final int TRANSIENT_BAR_NONE = 0;
- private static final int TRANSIENT_BAR_SHOW_REQUESTED = 1;
- private static final int TRANSIENT_BAR_SHOWING = 2;
- private static final int TRANSIENT_BAR_HIDING = 3;
-
- private static final int TRANSLUCENT_ANIMATION_DELAY_MS = 1000;
-
- private static final int MSG_NAV_BAR_VISIBILITY_CHANGED = 1;
-
- protected final String mTag;
- protected final int mDisplayId;
- private final int mTransientFlag;
- private final int mUnhideFlag;
- private final int mTranslucentFlag;
- private final int mTransparentFlag;
- private final int mStatusBarManagerId;
- private final int mTranslucentWmFlag;
private final int mWindowType;
- protected final Handler mHandler;
- private final Object mServiceAquireLock = new Object();
- private StatusBarManagerInternal mStatusBarInternal;
- protected WindowState mWin;
- private @StatusBarManager.WindowVisibleState int mState =
- StatusBarManager.WINDOW_STATE_SHOWING;
- private int mTransientBarState;
- private boolean mPendingShow;
- private long mLastTranslucent;
- private boolean mShowTransparent;
- private boolean mSetUnHideFlagWhenNextTransparent;
- private boolean mNoAnimationOnNextShow;
private final Rect mContentFrame = new Rect();
- private OnBarVisibilityChangedListener mVisibilityChangeListener;
-
- BarController(String tag, int displayId, int transientFlag, int unhideFlag, int translucentFlag,
- int statusBarManagerId, int windowType, int translucentWmFlag, int transparentFlag) {
- mTag = "BarController." + tag;
- mDisplayId = displayId;
- mTransientFlag = transientFlag;
- mUnhideFlag = unhideFlag;
- mTranslucentFlag = translucentFlag;
- mStatusBarManagerId = statusBarManagerId;
+ BarController(int windowType) {
mWindowType = windowType;
- mTranslucentWmFlag = translucentWmFlag;
- mTransparentFlag = transparentFlag;
- mHandler = new BarHandler();
- }
-
- void setWindow(WindowState win) {
- if (ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL) {
- // BarController gets replaced with InsetsPolicy in the full insets mode.
- return;
- }
- mWin = win;
}
/**
@@ -109,67 +40,6 @@
mContentFrame.set(frame);
}
- void setShowTransparent(boolean transparent) {
- if (transparent != mShowTransparent) {
- mShowTransparent = transparent;
- mSetUnHideFlagWhenNextTransparent = transparent;
- mNoAnimationOnNextShow = true;
- }
- }
-
- void showTransient() {
- if (mWin != null) {
- setTransientBarState(TRANSIENT_BAR_SHOW_REQUESTED);
- }
- }
-
- boolean isTransientShowing() {
- return mTransientBarState == TRANSIENT_BAR_SHOWING;
- }
-
- boolean isTransientShowRequested() {
- return mTransientBarState == TRANSIENT_BAR_SHOW_REQUESTED;
- }
-
- boolean wasRecentlyTranslucent() {
- return (SystemClock.uptimeMillis() - mLastTranslucent) < TRANSLUCENT_ANIMATION_DELAY_MS;
- }
-
- void adjustSystemUiVisibilityLw(int oldVis, int vis) {
- if (mWin != null && mTransientBarState == TRANSIENT_BAR_SHOWING
- && (vis & mTransientFlag) == 0) {
- // sysui requests hide
- setTransientBarState(TRANSIENT_BAR_HIDING);
- setBarShowingLw(false);
- } else if (mWin != null && (oldVis & mUnhideFlag) != 0 && (vis & mUnhideFlag) == 0) {
- // sysui ready to unhide
- setBarShowingLw(true);
- }
- }
-
- int applyTranslucentFlagLw(WindowState win, int vis, int oldVis) {
- if (mWin != null) {
- if (win != null) {
- int fl = PolicyControl.getWindowFlags(win, null);
- if ((fl & mTranslucentWmFlag) != 0) {
- vis |= mTranslucentFlag;
- } else {
- vis &= ~mTranslucentFlag;
- }
- if ((fl & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
- && isTransparentAllowed(win)) {
- vis |= mTransparentFlag;
- } else {
- vis &= ~mTransparentFlag;
- }
- } else {
- vis = (vis & ~mTranslucentFlag) | (oldVis & mTranslucentFlag);
- vis = (vis & ~mTransparentFlag) | (oldVis & mTransparentFlag);
- }
- }
- return vis;
- }
-
private Rect getContentFrame(@NonNull WindowState win) {
final Rect rotatedContentFrame = win.mToken.getFixedRotationBarContentFrame(mWindowType);
return rotatedContentFrame != null ? rotatedContentFrame : mContentFrame;
@@ -188,212 +58,4 @@
}
return win.letterboxNotIntersectsOrFullyContains(getContentFrame(win));
}
-
- boolean setBarShowingLw(final boolean show) {
- if (mWin == null) return false;
- if (show && mTransientBarState == TRANSIENT_BAR_HIDING) {
- mPendingShow = true;
- return false;
- }
- final boolean wasVis = mWin.isVisibleLw();
- final boolean wasAnim = mWin.isAnimatingLw();
- final boolean skipAnim = skipAnimation();
- final boolean change = show ? mWin.showLw(!mNoAnimationOnNextShow && !skipAnim)
- : mWin.hideLw(!mNoAnimationOnNextShow && !skipAnim);
- mNoAnimationOnNextShow = false;
- final int state = computeStateLw(wasVis, wasAnim, mWin, change);
- final boolean stateChanged = updateStateLw(state);
-
- if (change && (mVisibilityChangeListener != null)) {
- mHandler.obtainMessage(MSG_NAV_BAR_VISIBILITY_CHANGED, show ? 1 : 0, 0).sendToTarget();
- }
-
- return change || stateChanged;
- }
-
- void setOnBarVisibilityChangedListener(OnBarVisibilityChangedListener listener,
- boolean invokeWithState) {
- mVisibilityChangeListener = listener;
- if (invokeWithState) {
- // Optionally report the initial window state for initialization purposes
- mHandler.obtainMessage(MSG_NAV_BAR_VISIBILITY_CHANGED,
- (mState == StatusBarManager.WINDOW_STATE_SHOWING) ? 1 : 0, 0).sendToTarget();
- }
- }
-
- protected boolean skipAnimation() {
- return !mWin.isDrawn();
- }
-
- private @StatusBarManager.WindowVisibleState int computeStateLw(
- boolean wasVis, boolean wasAnim, WindowState win, boolean change) {
- if (win.isDrawn()) {
- final boolean vis = win.isVisibleLw();
- final boolean anim = win.isAnimatingLw();
- if (mState == StatusBarManager.WINDOW_STATE_HIDING && !change && !vis) {
- return StatusBarManager.WINDOW_STATE_HIDDEN;
- } else if (mState == StatusBarManager.WINDOW_STATE_HIDDEN && vis) {
- return StatusBarManager.WINDOW_STATE_SHOWING;
- } else if (change) {
- if (wasVis && vis && !wasAnim && anim) {
- return StatusBarManager.WINDOW_STATE_HIDING;
- } else {
- return StatusBarManager.WINDOW_STATE_SHOWING;
- }
- }
- }
- return mState;
- }
-
- private boolean updateStateLw(@StatusBarManager.WindowVisibleState final int state) {
- if (mWin != null && state != mState) {
- mState = state;
- if (DEBUG) Slog.d(mTag, "mState: " + StatusBarManager.windowStateToString(state));
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- StatusBarManagerInternal statusbar = getStatusBarInternal();
- if (statusbar != null) {
- statusbar.setWindowState(mDisplayId, mStatusBarManagerId, state);
- }
- }
- });
- return true;
- }
- return false;
- }
-
- boolean checkHiddenLw() {
- if (mWin != null && mWin.isDrawn()) {
- if (!mWin.isVisibleLw() && !mWin.isAnimatingLw()) {
- updateStateLw(StatusBarManager.WINDOW_STATE_HIDDEN);
- }
- if (mTransientBarState == TRANSIENT_BAR_HIDING && !mWin.isVisibleLw()) {
- // Finished animating out, clean up and reset style
- setTransientBarState(TRANSIENT_BAR_NONE);
- if (mPendingShow) {
- setBarShowingLw(true);
- mPendingShow = false;
- }
- return true;
- }
- }
- return false;
- }
-
- boolean checkShowTransientBarLw() {
- if (mTransientBarState == TRANSIENT_BAR_SHOWING) {
- if (DEBUG) Slog.d(mTag, "Not showing transient bar, already shown");
- return false;
- } else if (mTransientBarState == TRANSIENT_BAR_SHOW_REQUESTED) {
- if (DEBUG) Slog.d(mTag, "Not showing transient bar, already requested");
- return false;
- } else if (mWin == null) {
- if (DEBUG) Slog.d(mTag, "Not showing transient bar, bar doesn't exist");
- return false;
- } else if (mWin.isDisplayed()) {
- if (DEBUG) Slog.d(mTag, "Not showing transient bar, bar already visible");
- return false;
- } else {
- return true;
- }
- }
-
- int updateVisibilityLw(boolean transientAllowed, int oldVis, int vis) {
- if (mWin == null) return vis;
- if (isTransientShowing() || isTransientShowRequested()) { // transient bar requested
- if (transientAllowed) {
- vis |= mTransientFlag;
- if ((oldVis & mTransientFlag) == 0) {
- vis |= mUnhideFlag; // tell sysui we're ready to unhide
- }
- setTransientBarState(TRANSIENT_BAR_SHOWING); // request accepted
- } else {
- setTransientBarState(TRANSIENT_BAR_NONE); // request denied
- }
- }
- if (mShowTransparent) {
- vis |= mTransparentFlag;
- if (mSetUnHideFlagWhenNextTransparent) {
- vis |= mUnhideFlag;
- mSetUnHideFlagWhenNextTransparent = false;
- }
- }
- if (mTransientBarState != TRANSIENT_BAR_NONE) {
- vis |= mTransientFlag; // ignore clear requests until transition completes
- vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE; // never show transient bars in low profile
- }
- if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0
- || ((vis | oldVis) & mTransparentFlag) != 0) {
- mLastTranslucent = SystemClock.uptimeMillis();
- }
- return vis;
- }
-
- private void setTransientBarState(int state) {
- if (mWin != null && state != mTransientBarState) {
- if (mTransientBarState == TRANSIENT_BAR_SHOWING || state == TRANSIENT_BAR_SHOWING) {
- mLastTranslucent = SystemClock.uptimeMillis();
- }
- mTransientBarState = state;
- if (DEBUG) Slog.d(mTag, "mTransientBarState: " + transientBarStateToString(state));
- }
- }
-
- protected StatusBarManagerInternal getStatusBarInternal() {
- synchronized (mServiceAquireLock) {
- if (mStatusBarInternal == null) {
- mStatusBarInternal = LocalServices.getService(StatusBarManagerInternal.class);
- }
- return mStatusBarInternal;
- }
- }
-
- private static String transientBarStateToString(int state) {
- if (state == TRANSIENT_BAR_HIDING) return "TRANSIENT_BAR_HIDING";
- if (state == TRANSIENT_BAR_SHOWING) return "TRANSIENT_BAR_SHOWING";
- if (state == TRANSIENT_BAR_SHOW_REQUESTED) return "TRANSIENT_BAR_SHOW_REQUESTED";
- if (state == TRANSIENT_BAR_NONE) return "TRANSIENT_BAR_NONE";
- throw new IllegalArgumentException("Unknown state " + state);
- }
-
- void dumpDebug(ProtoOutputStream proto, long fieldId) {
- final long token = proto.start(fieldId);
- proto.write(STATE, mState);
- proto.write(TRANSIENT_STATE, mTransientBarState);
- proto.end(token);
- }
-
- void dump(PrintWriter pw, String prefix) {
- if (mWin != null) {
- pw.print(prefix); pw.println(mTag);
- pw.print(prefix); pw.print(" "); pw.print("mState"); pw.print('=');
- pw.println(StatusBarManager.windowStateToString(mState));
- pw.print(prefix); pw.print(" "); pw.print("mTransientBar"); pw.print('=');
- pw.println(transientBarStateToString(mTransientBarState));
- pw.print(prefix); pw.print(" mContentFrame="); pw.println(mContentFrame);
- }
- }
-
- private class BarHandler extends Handler {
- BarHandler() {
- super(UiThread.getHandler().getLooper());
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_NAV_BAR_VISIBILITY_CHANGED:
- final boolean visible = msg.arg1 != 0;
- if (mVisibilityChangeListener != null) {
- mVisibilityChangeListener.onBarVisibilityChanged(visible);
- }
- break;
- }
- }
- }
-
- interface OnBarVisibilityChangedListener {
- void onBarVisibilityChanged(boolean visible);
- }
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4db121b..0044d74 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -192,7 +192,6 @@
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
import android.view.View;
-import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
@@ -3744,7 +3743,6 @@
void statusBarVisibilityChanged(int visibility) {
mLastStatusBarVisibility = visibility;
- visibility = getDisplayPolicy().adjustSystemUiVisibilityLw(visibility);
updateStatusBarVisibilityLocked(visibility);
}
@@ -3769,32 +3767,16 @@
void updateSystemUiVisibility(int visibility, int globalDiff) {
forAllWindows(w -> {
- try {
- final int curValue = w.mSystemUiVisibility;
- final int diff = (curValue ^ visibility) & globalDiff;
- final int newValue = (curValue & ~diff) | (visibility & diff);
- if (newValue != curValue) {
- w.mSeq++;
- w.mSystemUiVisibility = newValue;
- }
- if ((newValue != curValue || w.mAttrs.hasSystemUiListeners)
- && ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL) {
- w.mClient.dispatchSystemUiVisibilityChanged(w.mSeq,
- visibility, newValue, diff);
- }
- } catch (RemoteException e) {
- // so sorry
+ final int curValue = w.mSystemUiVisibility;
+ final int diff = (curValue ^ visibility) & globalDiff;
+ final int newValue = (curValue & ~diff) | (visibility & diff);
+ if (newValue != curValue) {
+ w.mSeq++;
+ w.mSystemUiVisibility = newValue;
}
}, true /* traverseTopToBottom */);
}
- void reevaluateStatusBarVisibility() {
- int visibility = getDisplayPolicy().adjustSystemUiVisibilityLw(mLastStatusBarVisibility);
- if (updateStatusBarVisibilityLocked(visibility)) {
- mWmService.mWindowPlacerLocked.requestTraversal();
- }
- }
-
void onWindowFreezeTimeout() {
Slog.w(TAG_WM, "Window freeze timeout expired.");
mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
@@ -3811,7 +3793,7 @@
mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
- // TODO: Super crazy long method that should be broken down...
+ // TODO: Super unexpected long method that should be broken down...
void applySurfaceChangesTransaction() {
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 779f6b2..572c9b3 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -57,8 +57,6 @@
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
-import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
@@ -66,13 +64,11 @@
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_IS_SCREEN_DECOR;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
@@ -134,14 +130,13 @@
import android.hardware.input.InputManager;
import android.hardware.power.Boost;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.ArraySet;
-import android.util.IntArray;
-import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
@@ -199,7 +194,6 @@
*/
public class DisplayPolicy {
private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayPolicy" : TAG_WM;
- private static final boolean DEBUG = false;
private static final boolean ALTERNATE_CAR_MODE_NAV_SIZE = false;
@@ -221,18 +215,6 @@
/** Use the transit animation in style resource (see {@link #selectAnimation}). */
static final int ANIMATION_STYLEABLE = 0;
- /**
- * These are the system UI flags that, when changing, can cause the layout
- * of the screen to change.
- */
- private static final int SYSTEM_UI_CHANGING_LAYOUT =
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.STATUS_BAR_TRANSLUCENT
- | View.NAVIGATION_BAR_TRANSLUCENT
- | View.STATUS_BAR_TRANSPARENT
- | View.NAVIGATION_BAR_TRANSPARENT;
-
private static final int[] SHOW_TYPES_FOR_SWIPE = {ITYPE_NAVIGATION_BAR, ITYPE_STATUS_BAR};
private static final int[] SHOW_TYPES_FOR_PANIC = {ITYPE_NAVIGATION_BAR};
@@ -327,21 +309,10 @@
private boolean mLastImmersiveMode;
- private final StatusBarController mStatusBarController;
-
+ private StatusBarManagerInternal mStatusBarInternal;
+ private final BarController mStatusBarController;
private final BarController mNavigationBarController;
- private final BarController.OnBarVisibilityChangedListener mNavBarVisibilityListener =
- new BarController.OnBarVisibilityChangedListener() {
- @Override
- public void onBarVisibilityChanged(boolean visible) {
- if (mAccessibilityManager == null) {
- return;
- }
- mAccessibilityManager.notifyAccessibilityButtonVisibilityChanged(visible);
- }
- };
-
// The windows we were told about in focusChanged.
private WindowState mFocusedWindow;
private WindowState mLastFocusedWindow;
@@ -353,15 +324,8 @@
private boolean mLastNavVisible;
private boolean mLastNavTranslucent;
private boolean mLastNavAllowedHidden;
- private boolean mLastNotificationShadeForcesShowingNavigation;
- int mLastSystemUiFlags;
- // Bits that we are in the process of clearing, so we want to prevent
- // them from being set by applications until everything has been updated
- // to have them clear.
- private int mResettingSystemUiFlags = 0;
- // Bits that we are currently always keeping cleared.
- private int mForceClearedSystemUiFlags = 0;
+ private int mLastDisableFlags;
private int mLastAppearance;
private int mLastFullscreenAppearance;
private int mLastDockedAppearance;
@@ -430,6 +394,8 @@
private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
+ private final WindowManagerInternal.AppTransitionListener mAppTransitionListener;
+
private class PolicyHandler extends Handler {
PolicyHandler(Looper looper) {
@@ -472,16 +438,9 @@
mLock = service.getWindowManagerLock();
final int displayId = displayContent.getDisplayId();
- mStatusBarController = new StatusBarController(displayId);
- mNavigationBarController = new BarController("NavigationBar",
- displayId,
- View.NAVIGATION_BAR_TRANSIENT,
- View.NAVIGATION_BAR_UNHIDE,
- View.NAVIGATION_BAR_TRANSLUCENT,
- StatusBarManager.WINDOW_NAVIGATION_BAR,
- TYPE_NAVIGATION_BAR,
- FLAG_TRANSLUCENT_NAVIGATION,
- View.NAVIGATION_BAR_TRANSPARENT);
+
+ mStatusBarController = new BarController(TYPE_STATUS_BAR);
+ mNavigationBarController = new BarController(TYPE_NAVIGATION_BAR);
final Resources r = mContext.getResources();
mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer);
@@ -612,8 +571,58 @@
}
});
displayContent.registerPointerEventListener(mSystemGestures);
- displayContent.mAppTransition.registerListenerLocked(
- mStatusBarController.getAppTransitionListener());
+ mAppTransitionListener = new WindowManagerInternal.AppTransitionListener() {
+
+ private Runnable mAppTransitionPending = () -> {
+ StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+ if (statusBar != null) {
+ statusBar.appTransitionPending(displayId);
+ }
+ };
+
+ private Runnable mAppTransitionCancelled = () -> {
+ StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+ if (statusBar != null) {
+ statusBar.appTransitionCancelled(displayId);
+ }
+ };
+
+ private Runnable mAppTransitionFinished = () -> {
+ StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+ if (statusBar != null) {
+ statusBar.appTransitionFinished(displayId);
+ }
+ };
+
+ @Override
+ public void onAppTransitionPendingLocked() {
+ mHandler.post(mAppTransitionPending);
+ }
+
+ @Override
+ public int onAppTransitionStartingLocked(int transit, long duration,
+ long statusBarAnimationStartTime, long statusBarAnimationDuration) {
+ mHandler.post(() -> {
+ StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+ if (statusBar != null) {
+ statusBar.appTransitionStarting(mContext.getDisplayId(),
+ statusBarAnimationStartTime, statusBarAnimationDuration);
+ }
+ });
+ return 0;
+ }
+
+ @Override
+ public void onAppTransitionCancelledLocked(int transit) {
+ mHandler.post(mAppTransitionCancelled);
+ }
+
+ @Override
+ public void onAppTransitionFinishedLocked(IBinder token) {
+ mHandler.post(mAppTransitionFinished);
+ }
+ };
+ displayContent.mAppTransition.registerListenerLocked(mAppTransitionListener);
mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext, looper,
mService.mVrModeEnabled);
@@ -1066,7 +1075,6 @@
break;
case TYPE_STATUS_BAR:
mStatusBar = win;
- mStatusBarController.setWindow(win);
final TriConsumer<DisplayFrames, WindowState, Rect> frameProvider =
(displayFrames, windowState, rect) -> {
rect.bottom = rect.top + getStatusBarHeight(displayFrames);
@@ -1077,9 +1085,6 @@
break;
case TYPE_NAVIGATION_BAR:
mNavigationBar = win;
- mNavigationBarController.setWindow(win);
- mNavigationBarController.setOnBarVisibilityChangedListener(
- mNavBarVisibilityListener, true);
mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win,
(displayFrames, windowState, inOutFrame) -> {
@@ -1135,12 +1140,10 @@
switch (insetsType) {
case ITYPE_STATUS_BAR:
mStatusBarAlt = win;
- mStatusBarController.setWindow(mStatusBarAlt);
mStatusBarAltPosition = getAltBarPosition(attrs);
break;
case ITYPE_NAVIGATION_BAR:
mNavigationBarAlt = win;
- mNavigationBarController.setWindow(mNavigationBarAlt);
mNavigationBarAltPosition = getAltBarPosition(attrs);
break;
}
@@ -1210,12 +1213,10 @@
if (mStatusBar == win || mStatusBarAlt == win) {
mStatusBar = null;
mStatusBarAlt = null;
- mStatusBarController.setWindow(null);
mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, null, null);
} else if (mNavigationBar == win || mNavigationBarAlt == win) {
mNavigationBar = null;
mNavigationBarAlt = null;
- mNavigationBarController.setWindow(null);
mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, null, null);
} else if (mNotificationShade == win) {
mNotificationShade = null;
@@ -1235,7 +1236,7 @@
}
@VisibleForTesting
- StatusBarController getStatusBarController() {
+ BarController getStatusBarController() {
return mStatusBarController;
}
@@ -1368,25 +1369,6 @@
}
/**
- * Called when a new system UI visibility is being reported, allowing
- * the policy to adjust what is actually reported.
- * @param visibility The raw visibility reported by the status bar.
- * @return The new desired visibility.
- */
- public int adjustSystemUiVisibilityLw(int visibility) {
- mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
- mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
-
- // Reset any bits in mForceClearingStatusBarVisibility that
- // are now clear.
- mResettingSystemUiFlags &= visibility;
- // Clear any bits in the new visibility that are currently being
- // force cleared, before reporting it.
- return visibility & ~mResettingSystemUiFlags
- & ~mForceClearedSystemUiFlags;
- }
-
- /**
* @return true if the system bars are forced to stay visible
*/
public boolean areSystemBarsForcedShownLw(WindowState windowState) {
@@ -1481,16 +1463,6 @@
return mForceShowSystemBars;
}
- private final Runnable mClearHideNavigationFlag = new Runnable() {
- @Override
- public void run() {
- synchronized (mLock) {
- mForceClearedSystemUiFlags &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
- mDisplayContent.reevaluateStatusBarVisibility();
- }
- }
- };
-
/**
* Input handler used while nav bar is hidden. Captures any touch on the screen,
* to determine when the nav bar should be shown and prevent applications from
@@ -1515,32 +1487,6 @@
return;
}
showSystemBars();
- // Any user activity always causes us to show the
- // navigation controls, if they had been hidden.
- // We also clear the low profile and only content
- // flags so that tapping on the screen will atomically
- // restore all currently hidden screen decorations.
- int newVal = mResettingSystemUiFlags
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_LOW_PROFILE
- | View.SYSTEM_UI_FLAG_FULLSCREEN;
- if (mResettingSystemUiFlags != newVal) {
- mResettingSystemUiFlags = newVal;
- changed = true;
- }
- // We don't allow the system's nav bar to be hidden
- // again for 1 second, to prevent applications from
- // spamming us and keeping it from being shown.
- newVal = mForceClearedSystemUiFlags
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
- if (mForceClearedSystemUiFlags != newVal) {
- mForceClearedSystemUiFlags = newVal;
- changed = true;
- mHandler.postDelayed(mClearHideNavigationFlag, 1000);
- }
- if (changed) {
- mDisplayContent.reevaluateStatusBarVisibility();
- }
}
}
}
@@ -1594,12 +1540,12 @@
contentFrame -> layoutNavigationBar(displayFrames,
mDisplayContent.getConfiguration().uiMode, mLastNavVisible,
mLastNavTranslucent, mLastNavAllowedHidden,
- mLastNotificationShadeForcesShowingNavigation, contentFrame));
+ contentFrame));
}
if (mStatusBar != null) {
simulateLayoutDecorWindow(mStatusBar, displayFrames, insetsState,
simulatedWindowFrames, barContentFrames,
- contentFrame -> layoutStatusBar(displayFrames, mLastSystemUiFlags,
+ contentFrame -> layoutStatusBar(displayFrames, mLastAppearance,
contentFrame));
}
layoutScreenDecorWindows(displayFrames, simulatedWindowFrames);
@@ -1621,25 +1567,17 @@
// For purposes of putting out fake window up to steal focus, we will
// drive nav being hidden only by whether it is requested.
- final int sysui = mLastSystemUiFlags;
+ final int appearance = mLastAppearance;
final int behavior = mLastBehavior;
final InsetsSourceProvider provider =
mDisplayContent.getInsetsStateController().peekSourceProvider(ITYPE_NAVIGATION_BAR);
boolean navVisible = provider != null ? provider.isClientVisible()
: InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR);
- boolean navTranslucent = (sysui
- & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0;
- boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0
- || (behavior & BEHAVIOR_SHOW_BARS_BY_SWIPE) != 0;
- boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0
- || (behavior & BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) != 0;
+ boolean navTranslucent = (appearance & APPEARANCE_OPAQUE_NAVIGATION_BARS) == 0;
+ boolean immersive = (behavior & BEHAVIOR_SHOW_BARS_BY_SWIPE) != 0;
+ boolean immersiveSticky = (behavior & BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) != 0;
boolean navAllowedHidden = immersive || immersiveSticky;
navTranslucent &= !immersiveSticky; // transient trumps translucent
- boolean isKeyguardShowing = isKeyguardShowing() && !isKeyguardOccluded();
- boolean notificationShadeForcesShowingNavigation =
- !isKeyguardShowing && mNotificationShade != null
- && (mNotificationShade.getAttrs().privateFlags
- & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
updateHideNavInputEventReceiver();
@@ -1647,21 +1585,15 @@
// be hidden (because of the screen aspect ratio), then take that into account.
navVisible |= !canHideNavigationBar();
- boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, navVisible,
- navTranslucent, navAllowedHidden, notificationShadeForcesShowingNavigation,
- null /* simulatedContentFrame */);
+ layoutNavigationBar(displayFrames, uiMode, navVisible,
+ navTranslucent, navAllowedHidden, null /* simulatedContentFrame */);
if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);
- updateSysUiVisibility |= layoutStatusBar(displayFrames, sysui,
- null /* simulatedContentFrame */);
- if (updateSysUiVisibility) {
- updateSystemUiVisibilityLw();
- }
+ layoutStatusBar(displayFrames, appearance, null /* simulatedContentFrame */);
layoutScreenDecorWindows(displayFrames, null /* simulatedFrames */);
postAdjustDisplayFrames(displayFrames);
mLastNavVisible = navVisible;
mLastNavTranslucent = navTranslucent;
mLastNavAllowedHidden = navAllowedHidden;
- mLastNotificationShadeForcesShowingNavigation = notificationShadeForcesShowingNavigation;
}
void updateHideNavInputEventReceiver() {
@@ -1825,11 +1757,11 @@
displayFrames.mContent.set(dockFrame);
}
- private boolean layoutStatusBar(DisplayFrames displayFrames, int sysui,
+ private void layoutStatusBar(DisplayFrames displayFrames, int appearance,
Rect simulatedContentFrame) {
// decide where the status bar goes ahead of time
if (mStatusBar == null) {
- return false;
+ return;
}
// apply any status bar insets
getRotatedWindowBounds(displayFrames, mStatusBar, sTmpStatusFrame);
@@ -1860,10 +1792,9 @@
mStatusBarController.setContentFrame(sTmpRect);
}
- boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0
- || mDisplayContent.getInsetsPolicy().isTransient(ITYPE_STATUS_BAR);
- boolean statusBarTranslucent = (sysui
- & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0;
+ boolean statusBarTransient =
+ mDisplayContent.getInsetsPolicy().isTransient(ITYPE_STATUS_BAR);
+ boolean statusBarTranslucent = (appearance & APPEARANCE_OPAQUE_STATUS_BARS) == 0;
// If the status bar is hidden, we don't want to cause windows behind it to scroll.
if (mStatusBar.isVisibleLw() && !statusBarTransient) {
@@ -1879,8 +1810,7 @@
"dock=%s content=%s cur=%s", dockFrame.toString(),
displayFrames.mContent.toString(), displayFrames.mCurrent.toString()));
- if (!statusBarTranslucent && !mStatusBarController.wasRecentlyTranslucent()
- && !mStatusBar.isAnimatingLw()) {
+ if (!statusBarTranslucent && !mStatusBar.isAnimatingLw()) {
// If the opaque status bar is currently requested to be visible, and not in the
// process of animating on or off, then we can tell the app that it is covered by
@@ -1888,18 +1818,17 @@
displayFrames.mSystem.top = displayFrames.mStable.top;
}
}
- return mStatusBarController.checkHiddenLw();
}
- private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, boolean navVisible,
- boolean navTranslucent, boolean navAllowedHidden,
- boolean statusBarForcesShowingNavigation, Rect simulatedContentFrame) {
+ private void layoutNavigationBar(DisplayFrames displayFrames, int uiMode, boolean navVisible,
+ boolean navTranslucent, boolean navAllowedHidden, Rect simulatedContentFrame) {
if (mNavigationBar == null) {
- return false;
+ return;
}
final Rect navigationFrame = sTmpNavFrame;
- boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
+ boolean navBarTransient =
+ mDisplayContent.getInsetsPolicy().isTransient(ITYPE_NAVIGATION_BAR);
// Force the navigation bar to its appropriate place and size. We need to do this directly,
// instead of relying on it to bubble up from the nav bar, because this needs to change
// atomically with screen rotations.
@@ -1924,18 +1853,11 @@
- getNavigationBarHeight(rotation, uiMode);
navigationFrame.top = topNavBar;
displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top;
- if (transientNavBarShowing) {
- mNavigationBarController.setBarShowingLw(true);
- } else if (navVisible) {
- mNavigationBarController.setBarShowingLw(true);
+ if (navVisible && !navBarTransient) {
dockFrame.bottom = displayFrames.mRestricted.bottom = top;
- } else {
- // We currently want to hide the navigation UI - unless we expanded the status bar.
- mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
}
if (navVisible && !navTranslucent && !navAllowedHidden
- && !mNavigationBar.isAnimatingLw()
- && !mNavigationBarController.wasRecentlyTranslucent()) {
+ && !mNavigationBar.isAnimatingLw()) {
// If the opaque nav bar is currently requested to be visible and not in the process
// of animating on or off, then we can tell the app that it is covered by it.
displayFrames.mSystem.bottom = top;
@@ -1946,18 +1868,11 @@
- getNavigationBarWidth(rotation, uiMode);
navigationFrame.left = left;
displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left;
- if (transientNavBarShowing) {
- mNavigationBarController.setBarShowingLw(true);
- } else if (navVisible) {
- mNavigationBarController.setBarShowingLw(true);
+ if (navVisible && !navBarTransient) {
dockFrame.right = displayFrames.mRestricted.right = left;
- } else {
- // We currently want to hide the navigation UI - unless we expanded the status bar.
- mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
}
if (navVisible && !navTranslucent && !navAllowedHidden
- && !mNavigationBar.isAnimatingLw()
- && !mNavigationBarController.wasRecentlyTranslucent()) {
+ && !mNavigationBar.isAnimatingLw()) {
// If the nav bar is currently requested to be visible, and not in the process of
// animating on or off, then we can tell the app that it is covered by it.
displayFrames.mSystem.right = left;
@@ -1968,18 +1883,11 @@
+ getNavigationBarWidth(rotation, uiMode);
navigationFrame.right = right;
displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right;
- if (transientNavBarShowing) {
- mNavigationBarController.setBarShowingLw(true);
- } else if (navVisible) {
- mNavigationBarController.setBarShowingLw(true);
+ if (navVisible && !navBarTransient) {
dockFrame.left = displayFrames.mRestricted.left = right;
- } else {
- // We currently want to hide the navigation UI - unless we expanded the status bar.
- mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
}
if (navVisible && !navTranslucent && !navAllowedHidden
- && !mNavigationBar.isAnimatingLw()
- && !mNavigationBarController.wasRecentlyTranslucent()) {
+ && !mNavigationBar.isAnimatingLw()) {
// If the nav bar is currently requested to be visible, and not in the process of
// animating on or off, then we can tell the app that it is covered by it.
displayFrames.mSystem.left = right;
@@ -2008,7 +1916,6 @@
}
if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame);
- return mNavigationBarController.checkHiddenLw();
}
private boolean canReceiveInput(WindowState win) {
@@ -2059,9 +1966,6 @@
dcf.setEmpty();
windowFrames.setParentFrameWasClippedByDisplayCutout(false);
- final boolean hasNavBar = hasNavigationBar() && mNavigationBar != null
- && mNavigationBar.isVisibleLw();
-
final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
@@ -2407,53 +2311,27 @@
+ " top=" + mTopFullscreenOpaqueWindowState);
final boolean forceShowStatusBar = (getStatusBar().getAttrs().privateFlags
& PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR) != 0;
- final boolean notificationShadeForcesShowingNavigation =
- mNotificationShade != null
- && (mNotificationShade.getAttrs().privateFlags
- & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
boolean topAppHidesStatusBar = topAppHidesStatusBar();
if (mForceStatusBar || forceShowStatusBar) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Showing status bar: forced");
- if (mStatusBarController.setBarShowingLw(true)) {
- changes |= FINISH_LAYOUT_REDO_LAYOUT;
- }
// Maintain fullscreen layout until incoming animation is complete.
topIsFullscreen = mTopIsFullscreen && mStatusBar.isAnimatingLw();
- // Transient status bar is not allowed if notification shade is expecting the
- // navigation keys from the user.
- if (notificationShadeForcesShowingNavigation
- && mStatusBarController.isTransientShowing()) {
- mStatusBarController.updateVisibilityLw(false /*transientAllowed*/,
- mLastSystemUiFlags, mLastSystemUiFlags);
- }
} else if (mTopFullscreenOpaqueWindowState != null) {
topIsFullscreen = topAppHidesStatusBar;
// The subtle difference between the window for mTopFullscreenOpaqueWindowState
// and mTopIsFullscreen is that mTopIsFullscreen is set only if the window
// has the FLAG_FULLSCREEN set. Not sure if there is another way that to be the
// case though.
- if (mStatusBarController.isTransientShowing()) {
- if (mStatusBarController.setBarShowingLw(true)) {
- changes |= FINISH_LAYOUT_REDO_LAYOUT;
- }
- } else if (topIsFullscreen && !mDisplayContent.getDefaultTaskDisplayArea()
+ if (!topIsFullscreen || mDisplayContent.getDefaultTaskDisplayArea()
.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar");
- if (mStatusBarController.setBarShowingLw(false)) {
- changes |= FINISH_LAYOUT_REDO_LAYOUT;
- } else {
- if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar already hiding");
- }
- } else {
- if (DEBUG_LAYOUT) Slog.v(TAG, "** SHOWING status bar: top is not fullscreen");
- if (mStatusBarController.setBarShowingLw(true)) {
- changes |= FINISH_LAYOUT_REDO_LAYOUT;
- }
topAppHidesStatusBar = false;
}
}
- mStatusBarController.setTopAppHidesStatusBar(topAppHidesStatusBar);
+ StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+ if (statusBar != null) {
+ statusBar.setTopAppHidesStatusBar(topAppHidesStatusBar);
+ }
}
if (mTopIsFullscreen != topIsFullscreen) {
@@ -2464,7 +2342,7 @@
mTopIsFullscreen = topIsFullscreen;
}
- if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) {
+ if (updateSystemUiVisibilityLw()) {
// If the navigation bar has been hidden or shown, we need to do another
// layout pass to update that window.
changes |= FINISH_LAYOUT_REDO_LAYOUT;
@@ -2497,7 +2375,6 @@
Slog.d(TAG, "attr: " + attrs + " request: " + request);
}
return (fl & LayoutParams.FLAG_FULLSCREEN) != 0
- || (sysui & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0
|| (request != null && !request.isVisible());
}
@@ -2910,7 +2787,7 @@
if (mDisplayContent.isDefaultDisplay) {
mService.mPolicy.onDefaultDisplayFocusChangedLw(newFocus);
}
- if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) {
+ if (updateSystemUiVisibilityLw()) {
// If the navigation bar has been hidden or shown, we need to do another
// layout pass to update that window.
return FINISH_LAYOUT_REDO_LAYOUT;
@@ -2977,17 +2854,20 @@
}
void resetSystemUiVisibilityLw() {
- mLastSystemUiFlags = 0;
+ mLastDisableFlags = 0;
updateSystemUiVisibilityLw();
}
- int updateSystemUiVisibilityLw() {
+ /**
+ * @return {@code true} if the update may affect the layout.
+ */
+ boolean updateSystemUiVisibilityLw() {
// If there is no window focused, there will be nobody to handle the events
// anyway, so just hang on in whatever state we're in until things settle down.
WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow
: mTopFullscreenOpaqueWindowState;
if (winCandidate == null) {
- return 0;
+ return false;
}
// The immersive mode confirmation should never affect the system bar visibility, otherwise
@@ -3003,7 +2883,7 @@
: lastFocusCanReceiveKeys ? mLastFocusedWindow
: mTopFullscreenOpaqueWindowState;
if (winCandidate == null) {
- return 0;
+ return false;
}
}
final WindowState win = winCandidate;
@@ -3011,17 +2891,9 @@
mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
- int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)
- & ~mResettingSystemUiFlags
- & ~mForceClearedSystemUiFlags;
- if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) {
- tmpVisibility
- &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);
- }
-
- final int fullscreenAppearance = updateLightStatusBarAppearanceLw(0 /* vis */,
+ final int fullscreenAppearance = updateLightStatusBarLw(0 /* vis */,
mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
- final int dockedAppearance = updateLightStatusBarAppearanceLw(0 /* vis */,
+ final int dockedAppearance = updateLightStatusBarLw(0 /* vis */,
mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
final boolean inSplitScreen =
mService.mRoot.getDefaultTaskDisplayArea().isSplitScreenModeActivated();
@@ -3034,32 +2906,24 @@
mService.getStackBounds(inSplitScreen ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
: WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_UNDEFINED, mNonDockedStackBounds);
- final Pair<Integer, WindowState> result =
- updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
- final int visibility = result.first;
- final WindowState navColorWin = result.second;
+ final int disableFlags = win.getSystemUiVisibility() & StatusBarManager.DISABLE_MASK;
+ final int opaqueAppearance = updateSystemBarsLw(win, disableFlags);
+ final WindowState navColorWin = chooseNavigationColorWindowLw(
+ mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState,
+ mDisplayContent.mInputMethodWindow, mNavigationBarPosition);
final boolean isNavbarColorManagedByIme =
navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow;
- final int opaqueAppearance = InsetsFlags.getAppearance(visibility)
- & (APPEARANCE_OPAQUE_STATUS_BARS | APPEARANCE_OPAQUE_NAVIGATION_BARS);
- final int appearance = updateLightNavigationBarAppearanceLw(
+ final int appearance = updateLightNavigationBarLw(
win.mAttrs.insetsFlags.appearance, mTopFullscreenOpaqueWindowState,
mTopFullscreenOpaqueOrDimmingWindowState,
mDisplayContent.mInputMethodWindow, navColorWin) | opaqueAppearance;
- final int diff = visibility ^ mLastSystemUiFlags;
- final InsetsPolicy insetsPolicy = getInsetsPolicy();
- final boolean isFullscreen = (visibility & (View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)) != 0
- || (PolicyControl.getWindowFlags(win, win.mAttrs) & FLAG_FULLSCREEN) != 0
- || (getStatusBar() != null && insetsPolicy.isHidden(ITYPE_STATUS_BAR))
- || (getNavigationBar() != null && insetsPolicy.isHidden(
- ITYPE_NAVIGATION_BAR));
+ final InsetsState requestedInsets = win.getRequestedInsetsState();
final int behavior = win.mAttrs.insetsFlags.behavior;
- final boolean isImmersive = (visibility & (View.SYSTEM_UI_FLAG_IMMERSIVE
- | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)) != 0
- || behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE
+ final boolean isImmersive = behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE
|| behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
- if (diff == 0
+ final boolean isFullscreen = !requestedInsets.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR)
+ || !requestedInsets.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR);
+ if (mLastDisableFlags == disableFlags
&& mLastAppearance == appearance
&& mLastFullscreenAppearance == fullscreenAppearance
&& mLastDockedAppearance == dockedAppearance
@@ -3068,14 +2932,10 @@
&& mLastFocusIsImmersive == isImmersive
&& mLastNonDockedStackBounds.equals(mNonDockedStackBounds)
&& mLastDockedStackBounds.equals(mDockedStackBounds)) {
- return 0;
+ return false;
}
- // Obtains which types should show transient and which types should abort transient.
- // If there is no transient state change, this pair will contain two empty arrays.
- final Pair<int[], int[]> transientState = getTransientState(visibility, mLastSystemUiFlags);
-
- mLastSystemUiFlags = visibility;
+ mLastDisableFlags = disableFlags;
mLastAppearance = appearance;
mLastFullscreenAppearance = fullscreenAppearance;
mLastDockedAppearance = dockedAppearance;
@@ -3097,50 +2957,17 @@
StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
if (statusBar != null) {
final int displayId = getDisplayId();
- statusBar.setDisableFlags(displayId, visibility & StatusBarManager.DISABLE_MASK,
- cause);
- if (transientState.first.length > 0) {
- statusBar.showTransient(displayId, transientState.first);
- }
- if (transientState.second.length > 0) {
- statusBar.abortTransient(displayId, transientState.second);
- }
+ statusBar.setDisableFlags(displayId, disableFlags, cause);
statusBar.onSystemBarAppearanceChanged(displayId, appearance,
appearanceRegions, isNavbarColorManagedByIme);
statusBar.topAppWindowChanged(displayId, isFullscreen, isImmersive);
- // TODO(b/118118435): Remove this after removing system UI visibilities.
- synchronized (mLock) {
- mDisplayContent.statusBarVisibilityChanged(
- visibility & ~(View.STATUS_BAR_UNHIDE | View.NAVIGATION_BAR_UNHIDE));
- }
}
});
- return diff;
+ return true;
}
- private static Pair<int[], int[]> getTransientState(int vis, int oldVis) {
- final IntArray typesToShow = new IntArray(0);
- final IntArray typesToAbort = new IntArray(0);
- updateTransientState(vis, oldVis, View.STATUS_BAR_TRANSIENT, ITYPE_STATUS_BAR, typesToShow,
- typesToAbort);
- updateTransientState(vis, oldVis, View.NAVIGATION_BAR_TRANSIENT,
- ITYPE_NAVIGATION_BAR, typesToShow, typesToAbort);
- return Pair.create(typesToShow.toArray(), typesToAbort.toArray());
- }
-
- private static void updateTransientState(int vis, int oldVis, int transientFlag,
- @InternalInsetsType int type, IntArray typesToShow, IntArray typesToAbort) {
- final boolean wasTransient = (oldVis & transientFlag) != 0;
- final boolean isTransient = (vis & transientFlag) != 0;
- if (!wasTransient && isTransient) {
- typesToShow.add(type);
- } else if (wasTransient && !isTransient) {
- typesToAbort.add(type);
- }
- }
-
- private int updateLightStatusBarAppearanceLw(@Appearance int appearance, WindowState opaque,
+ private int updateLightStatusBarLw(@Appearance int appearance, WindowState opaque,
WindowState opaqueOrDimming) {
final boolean onKeyguard = isKeyguardShowing() && !isKeyguardOccluded();
final WindowState statusColorWin = onKeyguard ? mNotificationShade : opaqueOrDimming;
@@ -3207,24 +3034,7 @@
}
@VisibleForTesting
- static int updateLightNavigationBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming,
- WindowState imeWindow, WindowState navColorWin) {
-
- if (navColorWin != null) {
- if (navColorWin == imeWindow || navColorWin == opaque) {
- // Respect the light flag.
- vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
- vis |= PolicyControl.getSystemUiVisibility(navColorWin, null)
- & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
- } else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) {
- // Clear the light flag for dimming window.
- vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
- }
- }
- return vis;
- }
-
- private int updateLightNavigationBarAppearanceLw(int appearance, WindowState opaque,
+ int updateLightNavigationBarLw(int appearance, WindowState opaque,
WindowState opaqueOrDimming, WindowState imeWindow, WindowState navColorWin) {
if (navColorWin != null) {
@@ -3244,7 +3054,7 @@
return appearance;
}
- private Pair<Integer, WindowState> updateSystemBarsLw(WindowState win, int oldVis, int vis) {
+ private int updateSystemBarsLw(WindowState win, int disableFlags) {
final boolean dockedStackVisible = mDisplayContent.getDefaultTaskDisplayArea()
.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final boolean freeformStackVisible = mDisplayContent.getDefaultTaskDisplayArea()
@@ -3257,115 +3067,46 @@
mForceShowSystemBars = dockedStackVisible || win.inFreeformWindowingMode() || resizing;
final boolean forceOpaqueStatusBar = mForceShowSystemBars && !isKeyguardShowing();
- // apply translucent bar vis flags
- WindowState fullscreenTransWin = isKeyguardShowing() && !isKeyguardOccluded()
- ? mNotificationShade
- : mTopFullscreenOpaqueWindowState;
- vis = mStatusBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
- vis = mNavigationBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
- int dockedVis = mStatusBarController.applyTranslucentFlagLw(
- mTopDockedOpaqueWindowState, 0, 0);
- dockedVis = mNavigationBarController.applyTranslucentFlagLw(
- mTopDockedOpaqueWindowState, dockedVis, 0);
-
final boolean fullscreenDrawsStatusBarBackground =
- drawsStatusBarBackground(vis, mTopFullscreenOpaqueWindowState);
+ drawsStatusBarBackground(mTopFullscreenOpaqueWindowState);
final boolean dockedDrawsStatusBarBackground =
- drawsStatusBarBackground(dockedVis, mTopDockedOpaqueWindowState);
+ drawsStatusBarBackground(mTopDockedOpaqueWindowState);
final boolean fullscreenDrawsNavBarBackground =
- drawsNavigationBarBackground(vis, mTopFullscreenOpaqueWindowState);
+ drawsNavigationBarBackground(mTopFullscreenOpaqueWindowState);
final boolean dockedDrawsNavigationBarBackground =
- drawsNavigationBarBackground(dockedVis, mTopDockedOpaqueWindowState);
+ drawsNavigationBarBackground(mTopDockedOpaqueWindowState);
- // prevent status bar interaction from clearing certain flags
- int type = win.getAttrs().type;
- boolean notificationShadeHasFocus = type == TYPE_NOTIFICATION_SHADE;
- if (notificationShadeHasFocus && !isKeyguardShowing()) {
- int flags = View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_IMMERSIVE
- | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
- | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
- if (isKeyguardOccluded()) {
- flags |= View.STATUS_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSLUCENT;
- }
- vis = (vis & ~flags) | (oldVis & flags);
- }
+ int appearance = APPEARANCE_OPAQUE_NAVIGATION_BARS | APPEARANCE_OPAQUE_STATUS_BARS;
if (fullscreenDrawsStatusBarBackground && dockedDrawsStatusBarBackground) {
- vis |= View.STATUS_BAR_TRANSPARENT;
- vis &= ~View.STATUS_BAR_TRANSLUCENT;
- } else if (forceOpaqueStatusBar) {
- vis &= ~(View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT);
+ appearance &= ~APPEARANCE_OPAQUE_STATUS_BARS;
}
- vis = configureNavBarOpacity(vis, dockedStackVisible, freeformStackVisible, resizing,
- fullscreenDrawsNavBarBackground, dockedDrawsNavigationBarBackground);
+ appearance = configureNavBarOpacity(appearance, dockedStackVisible,
+ freeformStackVisible, resizing, fullscreenDrawsNavBarBackground,
+ dockedDrawsNavigationBarBackground);
- // update status bar
- boolean immersiveSticky =
- (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
- final boolean hideStatusBarWM =
- mTopFullscreenOpaqueWindowState != null
- && (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)
- & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
- final boolean hideStatusBarSysui =
- (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
- final boolean hideNavBarSysui = (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
- // We shouldn't rely on the system UI visibilities anymore because the window can
- // use the new API (e.g., WindowInsetsController.hide) to hide navigation bar.
- // TODO(b/149813814): clean up the system UI flag usages in this function.
- || !win.getRequestedInsetsState().getSourceOrDefaultVisibility(
+ final InsetsState requestedInsetsState = win.getRequestedInsetsState();
+ final boolean requestHideNavBar = !requestedInsetsState.getSourceOrDefaultVisibility(
ITYPE_NAVIGATION_BAR);
- final boolean transientStatusBarAllowed = getStatusBar() != null
- && (notificationShadeHasFocus || (!mForceShowSystemBars
- && (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky))));
-
- final boolean transientNavBarAllowed = mNavigationBar != null
- && !mForceShowSystemBars && hideNavBarSysui && immersiveSticky;
-
final long now = SystemClock.uptimeMillis();
final boolean pendingPanic = mPendingPanicGestureUptime != 0
&& now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION;
final DisplayPolicy defaultDisplayPolicy =
mService.getDefaultDisplayContentLocked().getDisplayPolicy();
- if (pendingPanic && hideNavBarSysui && win != mNotificationShade
+ if (pendingPanic && requestHideNavBar && win != mNotificationShade
&& getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR)
// TODO (b/111955725): Show keyguard presentation on all external displays
&& defaultDisplayPolicy.isKeyguardDrawComplete()) {
// The user performed the panic gesture recently, we're about to hide the bars,
// we're no longer on the Keyguard and the screen is ready. We can now request the bars.
mPendingPanicGestureUptime = 0;
- if (!isNavBarEmpty(vis)) {
+ if (!isNavBarEmpty(disableFlags)) {
mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_PANIC);
}
}
- final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()
- && !transientStatusBarAllowed && hideStatusBarSysui;
- final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested()
- && !transientNavBarAllowed;
- if (denyTransientStatus || denyTransientNav || mForceShowSystemBars) {
- // clear the clearable flags instead
- clearClearableFlagsLw();
- vis &= ~View.SYSTEM_UI_CLEARABLE_FLAGS;
- }
-
- final boolean immersive = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
- immersiveSticky = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
- final boolean navAllowedHidden = immersive || immersiveSticky;
-
- if (hideNavBarSysui && !navAllowedHidden
- && mService.mPolicy.getWindowLayerLw(win)
- > mService.mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_CONSUMER)) {
- // We can't hide the navbar from this window otherwise the input consumer would not get
- // the input events.
- vis = (vis & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
- }
-
- vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis);
-
// update navigation bar
boolean oldImmersiveMode = mLastImmersiveMode;
boolean newImmersiveMode = isImmersiveMode(win);
@@ -3374,23 +3115,13 @@
final String pkg = win.getOwningPackage();
mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode,
mService.mPolicy.isUserSetupComplete(),
- isNavBarEmpty(win.getSystemUiVisibility()));
+ isNavBarEmpty(disableFlags));
}
- vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis);
-
- final WindowState navColorWin = chooseNavigationColorWindowLw(
- mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState,
- mDisplayContent.mInputMethodWindow, mNavigationBarPosition);
- vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState,
- mTopFullscreenOpaqueOrDimmingWindowState,
- mDisplayContent.mInputMethodWindow, navColorWin);
-
- return Pair.create(vis, navColorWin);
+ return appearance;
}
- private boolean drawsBarBackground(int vis, WindowState win, BarController controller,
- int translucentFlag) {
+ private boolean drawsBarBackground(WindowState win, BarController controller) {
if (!controller.isTransparentAllowed(win)) {
return false;
}
@@ -3403,73 +3134,59 @@
final boolean forceDrawsSystemBars =
(win.getAttrs().privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
- return forceDrawsSystemBars || drawsSystemBars && (vis & translucentFlag) == 0;
+ return forceDrawsSystemBars || drawsSystemBars;
}
- private boolean drawsStatusBarBackground(int vis, WindowState win) {
- return drawsBarBackground(vis, win, mStatusBarController, FLAG_TRANSLUCENT_STATUS);
+ private boolean drawsStatusBarBackground(WindowState win) {
+ return drawsBarBackground(win, mStatusBarController);
}
- private boolean drawsNavigationBarBackground(int vis, WindowState win) {
- return drawsBarBackground(vis, win, mNavigationBarController, FLAG_TRANSLUCENT_NAVIGATION);
+ private boolean drawsNavigationBarBackground(WindowState win) {
+ return drawsBarBackground(win, mNavigationBarController);
}
/**
* @return the current visibility flags with the nav-bar opacity related flags toggled based
* on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}.
*/
- private int configureNavBarOpacity(int visibility, boolean dockedStackVisible,
+ private int configureNavBarOpacity(int appearance, boolean dockedStackVisible,
boolean freeformStackVisible, boolean isDockedDividerResizing,
boolean fullscreenDrawsBackground, boolean dockedDrawsNavigationBarBackground) {
if (mNavBarOpacityMode == NAV_BAR_FORCE_TRANSPARENT) {
if (fullscreenDrawsBackground && dockedDrawsNavigationBarBackground) {
- visibility = setNavBarTransparentFlag(visibility);
+ appearance = clearNavBarOpaqueFlag(appearance);
} else if (dockedStackVisible) {
- visibility = setNavBarOpaqueFlag(visibility);
+ appearance = setNavBarOpaqueFlag(appearance);
}
} else if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) {
if (dockedStackVisible || freeformStackVisible || isDockedDividerResizing) {
if (mIsFreeformWindowOverlappingWithNavBar) {
- visibility = setNavBarTranslucentFlag(visibility);
+ appearance = clearNavBarOpaqueFlag(appearance);
} else {
- visibility = setNavBarOpaqueFlag(visibility);
+ appearance = setNavBarOpaqueFlag(appearance);
}
} else if (fullscreenDrawsBackground) {
- visibility = setNavBarTransparentFlag(visibility);
+ appearance = clearNavBarOpaqueFlag(appearance);
}
} else if (mNavBarOpacityMode == NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE) {
if (isDockedDividerResizing) {
- visibility = setNavBarOpaqueFlag(visibility);
+ appearance = setNavBarOpaqueFlag(appearance);
} else if (freeformStackVisible) {
- visibility = setNavBarTranslucentFlag(visibility);
+ appearance = clearNavBarOpaqueFlag(appearance);
} else {
- visibility = setNavBarOpaqueFlag(visibility);
+ appearance = setNavBarOpaqueFlag(appearance);
}
}
- return visibility;
+ return appearance;
}
- private int setNavBarOpaqueFlag(int visibility) {
- return visibility & ~(View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT);
+ private int setNavBarOpaqueFlag(int appearance) {
+ return appearance | APPEARANCE_OPAQUE_NAVIGATION_BARS;
}
- private int setNavBarTranslucentFlag(int visibility) {
- visibility &= ~View.NAVIGATION_BAR_TRANSPARENT;
- return visibility | View.NAVIGATION_BAR_TRANSLUCENT;
- }
-
- private int setNavBarTransparentFlag(int visibility) {
- visibility &= ~View.NAVIGATION_BAR_TRANSLUCENT;
- return visibility | View.NAVIGATION_BAR_TRANSPARENT;
- }
-
- private void clearClearableFlagsLw() {
- int newVal = mResettingSystemUiFlags | View.SYSTEM_UI_CLEARABLE_FLAGS;
- if (newVal != mResettingSystemUiFlags) {
- mResettingSystemUiFlags = newVal;
- mDisplayContent.reevaluateStatusBarVisibility();
- }
+ private int clearNavBarOpaqueFlag(int appearance) {
+ return appearance & ~APPEARANCE_OPAQUE_NAVIGATION_BARS;
}
private boolean isImmersiveMode(WindowState win) {
@@ -3520,7 +3237,7 @@
// taken over the whole screen.
boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(isScreenOn,
SystemClock.elapsedRealtime(), isImmersiveMode(mSystemUiControllingWindow),
- isNavBarEmpty(mLastSystemUiFlags));
+ isNavBarEmpty(mLastDisableFlags));
if (panic) {
mHandler.post(mHiddenNavPanic);
}
@@ -3579,14 +3296,9 @@
pw.print(prefix); pw.print("mKeyguardDrawComplete="); pw.print(mKeyguardDrawComplete);
pw.print(" mWindowManagerDrawComplete="); pw.println(mWindowManagerDrawComplete);
pw.print(prefix); pw.print("mHdmiPlugged="); pw.println(mHdmiPlugged);
- if (mLastSystemUiFlags != 0 || mResettingSystemUiFlags != 0
- || mForceClearedSystemUiFlags != 0) {
- pw.print(prefix); pw.print("mLastSystemUiFlags=0x");
- pw.print(Integer.toHexString(mLastSystemUiFlags));
- pw.print(" mResettingSystemUiFlags=0x");
- pw.print(Integer.toHexString(mResettingSystemUiFlags));
- pw.print(" mForceClearedSystemUiFlags=0x");
- pw.println(Integer.toHexString(mForceClearedSystemUiFlags));
+ if (mLastDisableFlags != 0) {
+ pw.print(prefix); pw.print("mLastDisableFlags=0x");
+ pw.print(Integer.toHexString(mLastDisableFlags));
}
pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen);
@@ -3635,8 +3347,6 @@
pw.print(prefix); pw.print("mRemoteInsetsControllerControlsSystemBars");
pw.print(mDisplayContent.getInsetsPolicy().getRemoteInsetsControllerControlsSystemBars());
pw.print(" mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn);
- mStatusBarController.dump(pw, prefix);
- mNavigationBarController.dump(pw, prefix);
pw.print(prefix); pw.println("Looper state:");
mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + " ");
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 8d20bb8..2a5bf16 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -134,10 +134,13 @@
final int mOwnerUid;
final int mOwnerPid;
final WindowManagerService mWmService;
+ final int mDisplayId;
+ public Session mSession;
InputChannel mInputChannel;
final int mWindowType;
/**
+ * @param session calling session to check ownership of the window
* @param clientToken client token used to clean up the map if the embedding process dies
* @param hostWindowState input channel token belonging to the host window. This is needed
* to handle input callbacks to wm. It's used when raising ANR and
@@ -145,9 +148,13 @@
* can be null if there is no host window.
* @param ownerUid calling uid
* @param ownerPid calling pid used for anr blaming
+ * @param windowType to forward to input
+ * @param displayId used for focus requests
*/
- EmbeddedWindow(WindowManagerService service, IWindow clientToken,
- WindowState hostWindowState, int ownerUid, int ownerPid, int windowType) {
+ EmbeddedWindow(Session session, WindowManagerService service, IWindow clientToken,
+ WindowState hostWindowState, int ownerUid, int ownerPid, int windowType,
+ int displayId) {
+ mSession = session;
mWmService = service;
mClient = clientToken;
mHostWindowState = hostWindowState;
@@ -156,6 +163,7 @@
mOwnerUid = ownerUid;
mOwnerPid = ownerPid;
mWindowType = windowType;
+ mDisplayId = displayId;
}
String getName() {
diff --git a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java b/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
index d9cf637..aac6b25 100644
--- a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
+++ b/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
@@ -32,7 +32,7 @@
import java.io.PrintWriter;
/**
- * A Blacklist for packages that should force the display out of high refresh rate.
+ * A Denylist for packages that should force the display out of high refresh rate.
*/
class HighRefreshRateBlacklist {
@@ -72,7 +72,7 @@
}
} else {
// If there's no config, or the config has been deleted, fallback to the device's
- // default blacklist
+ // default denylist
for (String pkg : mDefaultBlacklist) {
mBlacklistedPackages.add(pkg);
}
diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
index 2cb7d5a..072116f 100644
--- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java
+++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
@@ -107,7 +107,7 @@
return mLowRefreshRateId;
}
- // If app is blacklisted using higher refresh rate, return default (lower) refresh rate
+ // If app is denylisted using higher refresh rate, return default (lower) refresh rate
if (mHighRefreshRateBlacklist.isBlacklisted(packageName)) {
return mLowRefreshRateId;
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 3d6d7b7..5f2113a 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -669,7 +669,7 @@
final long identity = Binder.clearCallingIdentity();
try {
- mService.grantInputChannel(mUid, mPid, displayId, surface, window, hostInputToken,
+ mService.grantInputChannel(this, mUid, mPid, displayId, surface, window, hostInputToken,
flags, mCanAddInternalSystemWindow ? privateFlags : 0,
mCanAddInternalSystemWindow ? type : 0, outInputChannel);
} finally {
@@ -688,4 +688,25 @@
Binder.restoreCallingIdentity(identity);
}
}
+
+ @Override
+ public void grantEmbeddedWindowFocus(IWindow callingWindow, IBinder targetInputToken,
+ boolean grantFocus) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (callingWindow == null) {
+ if (!mCanAddInternalSystemWindow) {
+ // Callers without INTERNAL_SYSTEM_WINDOW permission cannot request focus on
+ // embedded windows without providing the calling window
+ throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
+ }
+ mService.grantEmbeddedWindowFocus(this, targetInputToken, grantFocus);
+ } else {
+ mService.grantEmbeddedWindowFocus(this, callingWindow, targetInputToken,
+ grantFocus);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/StatusBarController.java b/services/core/java/com/android/server/wm/StatusBarController.java
deleted file mode 100644
index 3564e0b..0000000
--- a/services/core/java/com/android/server/wm/StatusBarController.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
-
-import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
-
-import android.app.StatusBarManager;
-import android.os.IBinder;
-import android.view.View;
-
-import com.android.server.statusbar.StatusBarManagerInternal;
-
-/**
- * Implements status bar specific behavior.
- */
-public class StatusBarController extends BarController {
-
- private final AppTransitionListener mAppTransitionListener = new AppTransitionListener() {
-
- private Runnable mAppTransitionPending = () -> {
- StatusBarManagerInternal statusBar = getStatusBarInternal();
- if (statusBar != null) {
- statusBar.appTransitionPending(mDisplayId);
- }
- };
-
- private Runnable mAppTransitionCancelled = () -> {
- StatusBarManagerInternal statusBar = getStatusBarInternal();
- if (statusBar != null) {
- statusBar.appTransitionCancelled(mDisplayId);
- }
- };
-
- private Runnable mAppTransitionFinished = () -> {
- StatusBarManagerInternal statusBar = getStatusBarInternal();
- if (statusBar != null) {
- statusBar.appTransitionFinished(mDisplayId);
- }
- };
-
- @Override
- public void onAppTransitionPendingLocked() {
- mHandler.post(mAppTransitionPending);
- }
-
- @Override
- public int onAppTransitionStartingLocked(int transit, long duration,
- long statusBarAnimationStartTime, long statusBarAnimationDuration) {
- mHandler.post(() -> {
- StatusBarManagerInternal statusBar = getStatusBarInternal();
- if (statusBar != null) {
- statusBar.appTransitionStarting(mDisplayId,
- statusBarAnimationStartTime, statusBarAnimationDuration);
- }
- });
- return 0;
- }
-
- @Override
- public void onAppTransitionCancelledLocked(int transit) {
- mHandler.post(mAppTransitionCancelled);
- }
-
- @Override
- public void onAppTransitionFinishedLocked(IBinder token) {
- mHandler.post(mAppTransitionFinished);
- }
- };
-
- StatusBarController(int displayId) {
- super("StatusBar",
- displayId,
- View.STATUS_BAR_TRANSIENT,
- View.STATUS_BAR_UNHIDE,
- View.STATUS_BAR_TRANSLUCENT,
- StatusBarManager.WINDOW_STATUS_BAR,
- TYPE_STATUS_BAR,
- FLAG_TRANSLUCENT_STATUS,
- View.STATUS_BAR_TRANSPARENT);
- }
-
- void setTopAppHidesStatusBar(boolean hidesStatusBar) {
- StatusBarManagerInternal statusBar = getStatusBarInternal();
- if (statusBar != null) {
- statusBar.setTopAppHidesStatusBar(hidesStatusBar);
- }
- }
-
- AppTransitionListener getAppTransitionListener() {
- return mAppTransitionListener;
- }
-}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 9172897..b7a5d05 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -425,8 +425,7 @@
int maxRecents;
/** Only used for persistable tasks, otherwise 0. The last time this task was moved. Used for
- * determining the order when restoring. Sign indicates whether last task movement was to front
- * (positive) or back (negative). Absolute value indicates time. */
+ * determining the order when restoring. */
long mLastTimeMoved;
/** If original intent did not allow relinquishing task identity, save that information */
@@ -952,6 +951,11 @@
removeImmediately();
if (isLeafTask()) {
mAtmService.getTaskChangeNotificationController().notifyTaskRemoved(mTaskId);
+
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
+ if (taskDisplayArea != null) {
+ taskDisplayArea.onLeafTaskRemoved(mTaskId);
+ }
}
}
@@ -1525,15 +1529,14 @@
mStackSupervisor.updateTopResumedActivityIfNeeded();
}
- void updateTaskMovement(boolean toFront) {
+ void updateTaskMovement(boolean toTop, int position) {
+ EventLogTags.writeWmTaskMoved(mTaskId, toTop ? 1 : 0, position);
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
+ if (taskDisplayArea != null && isLeafTask()) {
+ taskDisplayArea.onLeafTaskMoved(this, toTop);
+ }
if (isPersistable) {
mLastTimeMoved = System.currentTimeMillis();
- // Sign is used to keep tasks sorted when persisted. Tasks sent to the bottom most
- // recently will be most negative, tasks sent to the bottom before that will be less
- // negative. Similarly for recent tasks moved to the top which will be most positive.
- if (!toFront) {
- mLastTimeMoved *= -1;
- }
}
mRootWindowContainer.invalidateTaskLayers();
}
@@ -3180,6 +3183,7 @@
@Override
void positionChildAt(int position, WindowContainer child, boolean includingParents) {
+ final boolean toTop = position >= (mChildren.size() - 1);
position = getAdjustedChildPosition(child, position);
super.positionChildAt(position, child, includingParents);
@@ -3187,10 +3191,9 @@
if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "positionChildAt: child=" + child
+ " position=" + position + " parent=" + this);
- final int toTop = position >= (mChildren.size() - 1) ? 1 : 0;
final Task task = child.asTask();
if (task != null) {
- EventLogTags.writeWmTaskMoved(task.mTaskId, toTop, position);
+ task.updateTaskMovement(toTop, position);
}
}
@@ -5447,7 +5450,7 @@
// activities a chance to enter Pip before resuming the next activity.
final boolean lastResumedCanPip = prev != null && prev.checkEnterPictureInPictureState(
"shouldResumeWhilePausing", userLeaving);
- if (lastResumedCanPip && prev.pictureInPictureArgs.isAutoEnterAllowed()) {
+ if (lastResumedCanPip && prev.pictureInPictureArgs.isAutoEnterEnabled()) {
shouldAutoPip = true;
} else if (!lastResumedCanPip) {
pauseImmediately = true;
@@ -6877,9 +6880,6 @@
if (!deferResume) {
mRootWindowContainer.resumeFocusedStacksTopActivities();
}
- EventLogTags.writeWmTaskToFront(tr.mUserId, tr.mTaskId);
- mAtmService.getTaskChangeNotificationController()
- .notifyTaskMovedToFront(tr.getTaskInfo());
} finally {
mDisplayContent.continueUpdateImeTarget();
}
@@ -7265,7 +7265,6 @@
Slog.i(TAG_WM, "positionChildAt: positioning task=" + task + " at " + position);
}
positionChildAt(position, task, includingParents);
- task.updateTaskMovement(toTop);
getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
@@ -7402,7 +7401,6 @@
}
positionChildAt(POSITION_TOP, child, true /* includingParents */);
- child.updateTaskMovement(true);
final DisplayContent displayContent = getDisplayContent();
displayContent.layoutAndAssignWindowLayersIfNeeded();
@@ -7415,7 +7413,6 @@
final Task nextFocusableStack = getDisplayArea().getNextFocusableStack(
child.getRootTask(), true /* ignoreCurrent */);
positionChildAtBottom(child, nextFocusableStack == null /* includingParents */);
- child.updateTaskMovement(true);
}
@VisibleForTesting
@@ -7440,12 +7437,6 @@
}
final boolean isTop = getTopChild() == child;
-
- final Task task = child.asTask();
- if (task != null) {
- task.updateTaskMovement(isTop);
- }
-
if (isTop) {
final DisplayContent displayContent = getDisplayContent();
displayContent.layoutAndAssignWindowLayersIfNeeded();
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 2b32e40..890f56e 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -152,6 +153,12 @@
*/
private boolean mRemoved;
+
+ /**
+ * The id of a leaf task that most recently being moved to front.
+ */
+ private int mLastLeafTaskToFrontId;
+
TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name,
int displayAreaFeature) {
super(service, Type.ANY, name, displayAreaFeature);
@@ -382,7 +389,7 @@
this /* child */, true /* includingParents */);
}
- child.updateTaskMovement(moveToTop);
+ child.updateTaskMovement(moveToTop, targetPosition);
mDisplayContent.layoutAndAssignWindowLayersIfNeeded();
@@ -405,6 +412,30 @@
}
}
+ void onLeafTaskRemoved(int taskId) {
+ if (mLastLeafTaskToFrontId == taskId) {
+ mLastLeafTaskToFrontId = INVALID_TASK_ID;
+ }
+ }
+
+ void onLeafTaskMoved(Task t, boolean toTop) {
+ if (!toTop) {
+ if (t.mTaskId == mLastLeafTaskToFrontId) {
+ mLastLeafTaskToFrontId = INVALID_TASK_ID;
+ }
+ return;
+ }
+ if (t.mTaskId == mLastLeafTaskToFrontId || t.topRunningActivityLocked() == null) {
+ return;
+ }
+
+ mLastLeafTaskToFrontId = t.mTaskId;
+ EventLogTags.writeWmTaskToFront(t.mUserId, t.mTaskId);
+ // Notifying only when a leak task moved to front. Or the listeners would be notified
+ // couple times from the leaf task all the way up to the root task.
+ mAtmService.getTaskChangeNotificationController().notifyTaskMovedToFront(t.getTaskInfo());
+ }
+
@Override
boolean forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean> callback,
boolean traverseTopToBottom) {
diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java
index 20af250..a3dc290 100644
--- a/services/core/java/com/android/server/wm/TaskPersister.java
+++ b/services/core/java/com/android/server/wm/TaskPersister.java
@@ -197,7 +197,7 @@
mPersisterQueue.addItem(new TaskWriteQueueItem(task, mService), flush);
}
} else {
- // Dummy. Ensures removeObsoleteFiles is called when LazyTaskThreadWriter is
+ // Placeholder. Ensures removeObsoleteFiles is called when LazyTaskThreadWriter is
// notified.
mPersisterQueue.addItem(PersisterQueue.EMPTY_ITEM, flush);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8eb6432..8c2619d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3069,6 +3069,9 @@
if (!checkCallingPermission(permission.CONTROL_KEYGUARD, "dismissKeyguard")) {
throw new SecurityException("Requires CONTROL_KEYGUARD permission");
}
+ if (mAtmInternal.isDreaming()) {
+ mAtmService.mStackSupervisor.wakeUp("dismissKeyguard");
+ }
synchronized (mGlobalLock) {
mPolicy.dismissKeyguardLw(callback, message);
}
@@ -7974,16 +7977,17 @@
* Used by WindowlessWindowManager to enable input on SurfaceControl embedded
* views.
*/
- void grantInputChannel(int callingUid, int callingPid, int displayId, SurfaceControl surface,
- IWindow window, IBinder hostInputToken, int flags, int privateFlags, int type,
- InputChannel outInputChannel) {
+ void grantInputChannel(Session session, int callingUid, int callingPid, int displayId,
+ SurfaceControl surface, IWindow window, IBinder hostInputToken,
+ int flags, int privateFlags, int type, InputChannel outInputChannel) {
final InputApplicationHandle applicationHandle;
final String name;
final InputChannel clientChannel;
synchronized (mGlobalLock) {
EmbeddedWindowController.EmbeddedWindow win =
- new EmbeddedWindowController.EmbeddedWindow(this, window,
- mInputToWindowMap.get(hostInputToken), callingUid, callingPid, type);
+ new EmbeddedWindowController.EmbeddedWindow(session, this, window,
+ mInputToWindowMap.get(hostInputToken), callingUid, callingPid, type,
+ displayId);
clientChannel = win.openInputChannel();
mEmbeddedWindowController.add(clientChannel.getToken(), win);
applicationHandle = win.getApplicationHandle();
@@ -7998,19 +8002,19 @@
}
private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid,
- int displayId, SurfaceControl surface, String name,
- InputApplicationHandle applicationHandle, int flags, int privateFlags, int type,
- Region region) {
+ int displayId, SurfaceControl surface, String name,
+ InputApplicationHandle applicationHandle, int flags,
+ int privateFlags, int type, Region region) {
InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId);
h.token = channelToken;
h.name = name;
final int sanitizedFlags = flags & (LayoutParams.FLAG_NOT_TOUCHABLE
- | LayoutParams.FLAG_SLIPPERY);
+ | LayoutParams.FLAG_SLIPPERY | LayoutParams.FLAG_NOT_FOCUSABLE);
h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | sanitizedFlags;
h.layoutParamsType = type;
h.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
- h.focusable = false;
+ h.focusable = (flags & LayoutParams.FLAG_NOT_FOCUSABLE) == 0;
h.hasWallpaper = false;
h.paused = false;
@@ -8196,4 +8200,75 @@
Binder.restoreCallingIdentity(origId);
}
}
+
+ void grantEmbeddedWindowFocus(Session session, IBinder targetInputToken, boolean grantFocus) {
+ synchronized (mGlobalLock) {
+ final EmbeddedWindowController.EmbeddedWindow embeddedWindow =
+ mEmbeddedWindowController.get(targetInputToken);
+ if (embeddedWindow == null) {
+ Slog.e(TAG, "Embedded window not found");
+ return;
+ }
+ if (embeddedWindow.mSession != session) {
+ Slog.e(TAG, "Window not in session:" + session);
+ return;
+ }
+ SurfaceControl.Transaction t = mTransactionFactory.get();
+ final int displayId = embeddedWindow.mDisplayId;
+ if (grantFocus) {
+ t.setFocusedWindow(targetInputToken, displayId).apply();
+ } else {
+ // Search for a new focus target
+ DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ WindowState newFocusTarget = displayContent == null
+ ? null : displayContent.findFocusedWindow();
+ if (newFocusTarget == null) {
+ ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus remove request for "
+ + "win=%s dropped since no candidate was found",
+ embeddedWindow.getName());
+ return;
+ }
+ t.requestFocusTransfer(newFocusTarget.mInputWindowHandle.token, targetInputToken,
+ displayId).apply();
+ }
+ ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s",
+ embeddedWindow.getName(), grantFocus);
+ }
+ }
+
+ void grantEmbeddedWindowFocus(Session session, IWindow callingWindow, IBinder targetInputToken,
+ boolean grantFocus) {
+ synchronized (mGlobalLock) {
+ final WindowState hostWindow =
+ windowForClientLocked(session, callingWindow, false /* throwOnError*/);
+ if (hostWindow == null) {
+ Slog.e(TAG, "Host window not found");
+ return;
+ }
+ if (hostWindow.mInputChannel == null) {
+ Slog.e(TAG, "Host window does not have an input channel");
+ return;
+ }
+ final EmbeddedWindowController.EmbeddedWindow embeddedWindow =
+ mEmbeddedWindowController.get(targetInputToken);
+ if (embeddedWindow == null) {
+ Slog.e(TAG, "Embedded window not found");
+ return;
+ }
+ if (embeddedWindow.mHostWindowState != hostWindow) {
+ Slog.e(TAG, "Embedded window does not belong to the host");
+ return;
+ }
+ SurfaceControl.Transaction t = mTransactionFactory.get();
+ if (grantFocus) {
+ t.requestFocusTransfer(targetInputToken, hostWindow.mInputChannel.getToken(),
+ hostWindow.getDisplayId()).apply();
+ } else {
+ t.requestFocusTransfer(hostWindow.mInputChannel.getToken(), targetInputToken,
+ hostWindow.getDisplayId()).apply();
+ }
+ ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s",
+ embeddedWindow.getName(), grantFocus);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 271d2b1..506e0dd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -20,6 +20,7 @@
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.os.UserHandle;
@@ -32,10 +33,13 @@
import com.android.internal.os.ByteTransferPipe;
import com.android.internal.protolog.ProtoLogImpl;
+import com.android.server.LocalServices;
+import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
@@ -83,7 +87,21 @@
// trace files can be written.
return mInternal.mWindowTracing.onShellCommand(this);
case "logging":
- return ProtoLogImpl.getSingleInstance().onShellCommand(this);
+ String[] args = peekRemainingArgs();
+ int result = ProtoLogImpl.getSingleInstance().onShellCommand(this);
+ if (result != 0) {
+ // Let the shell try and handle this
+ try (ParcelFileDescriptor pfd
+ = ParcelFileDescriptor.dup(getOutFileDescriptor())){
+ pw.println("Not handled, calling status bar with args: "
+ + Arrays.toString(args));
+ LocalServices.getService(StatusBarManagerInternal.class)
+ .handleWindowManagerLoggingCommand(args, pfd);
+ } catch (IOException e) {
+ pw.println("Failed to handle logging command: " + e.getMessage());
+ }
+ }
+ return result;
case "set-user-rotation":
return runSetDisplayUserRotation(pw);
case "set-fix-to-user-rotation":
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cf73def..f2f3cef 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5863,6 +5863,10 @@
@Override
boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener,
int waitingId) {
+ // If the window is goneForLayout, relayout won't be called so we'd just wait forever.
+ if (isGoneForLayout()) {
+ return false;
+ }
boolean willSync = setPendingListener(waitingListener, waitingId);
if (!willSync) {
return false;
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index d84f9d1..1b649fd 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -37,6 +37,7 @@
"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_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index 95d4ba7..678308a 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -36,6 +36,9 @@
using android::base::StringPrintf;
using android::base::WriteStringToFile;
+#define SYNC_RECEIVED_WHILE_FROZEN (1)
+#define ASYNC_RECEIVED_WHILE_FROZEN (2)
+
namespace android {
// This performs per-process reclaim on all processes belonging to non-app UIDs.
@@ -99,12 +102,37 @@
}
}
+static jint com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo(JNIEnv *env,
+ jobject clazz, jint pid) {
+ bool syncReceived = false, asyncReceived = false;
+
+ int error = IPCThreadState::getProcessFreezeInfo(pid, &syncReceived, &asyncReceived);
+
+ if (error < 0) {
+ jniThrowException(env, "java/lang/RuntimeException", strerror(error));
+ }
+
+ jint retVal = 0;
+
+ if(syncReceived) {
+ retVal |= SYNC_RECEIVED_WHILE_FROZEN;;
+ }
+
+ if(asyncReceived) {
+ retVal |= ASYNC_RECEIVED_WHILE_FROZEN;
+ }
+
+ return retVal;
+}
+
static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
{"enableFreezerInternal", "(Z)V",
(void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal},
- {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder}
+ {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
+ {"getBinderFreezeInfo", "(I)I",
+ (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo}
};
int register_android_server_am_CachedAppOptimizer(JNIEnv* env)
diff --git a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp
new file mode 100644
index 0000000..5eb6b73
--- /dev/null
+++ b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp
@@ -0,0 +1,222 @@
+/*
+ * 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/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp
index 46e6f91..dda44fb 100644
--- a/services/core/jni/com_android_server_security_VerityUtils.cpp
+++ b/services/core/jni/com_android_server_security_VerityUtils.cpp
@@ -78,7 +78,7 @@
return -errno;
}
- // Sanity check.
+ // Validity check.
if ((out.stx_attributes_mask & STATX_ATTR_VERITY) == 0) {
ALOGE("Unexpected, STATX_ATTR_VERITY not supported by kernel");
return -ENOSYS;
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 5df1ada..e7f6db9 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -29,6 +29,7 @@
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);
@@ -82,6 +83,7 @@
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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 42bad3e..183a149 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -7867,15 +7867,21 @@
return null;
}
synchronized (getLockObject()) {
+ final ComponentName doComponent = mOwners.getDeviceOwnerComponent();
+ final ComponentName poComponent =
+ mOwners.getProfileOwnerComponent(userHandle.getIdentifier());
+ // Return test only admin by default.
+ if (isAdminTestOnlyLocked(doComponent, userHandle.getIdentifier())) {
+ return doComponent;
+ } else if (isAdminTestOnlyLocked(poComponent, userHandle.getIdentifier())) {
+ return poComponent;
+ }
final String supervisor = mContext.getResources().getString(
com.android.internal.R.string.config_defaultSupervisionProfileOwnerComponent);
if (supervisor == null) {
return null;
}
final ComponentName supervisorComponent = ComponentName.unflattenFromString(supervisor);
- final ComponentName doComponent = mOwners.getDeviceOwnerComponent();
- final ComponentName poComponent =
- mOwners.getProfileOwnerComponent(userHandle.getIdentifier());
if (supervisorComponent.equals(doComponent) || supervisorComponent.equals(
poComponent)) {
return supervisorComponent;
@@ -10225,6 +10231,12 @@
throw new SecurityException(
"User " + userId + " is not allowed to call setSecondaryLockscreenEnabled");
}
+ synchronized (getLockObject()) {
+ if (isAdminTestOnlyLocked(who, userId)) {
+ // Allow testOnly admins to bypass supervision config requirement.
+ return;
+ }
+ }
// Only the default supervision app can use this API.
final String supervisor = mContext.getResources().getString(
com.android.internal.R.string.config_defaultSupervisionProfileOwnerComponent);
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 87ae4d7..bf3a896 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -301,6 +301,20 @@
return ok();
}
+binder::Status BinderIncrementalService::registerLoadingProgressListener(
+ int32_t storageId,
+ const ::android::sp<::android::os::incremental::IStorageLoadingProgressListener>&
+ progressListener,
+ bool* _aidl_return) {
+ *_aidl_return = mImpl.registerLoadingProgressListener(storageId, progressListener);
+ return ok();
+}
+binder::Status BinderIncrementalService::unregisterLoadingProgressListener(int32_t storageId,
+ bool* _aidl_return) {
+ *_aidl_return = mImpl.unregisterLoadingProgressListener(storageId);
+ return ok();
+}
+
} // namespace android::os::incremental
jlong Incremental_IncrementalService_Start(JNIEnv* env) {
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index 8478142..1238498 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -81,6 +81,12 @@
const std::string& abi, bool extractNativeLibs,
bool* _aidl_return) final;
binder::Status waitForNativeBinariesExtraction(int storageId, bool* _aidl_return) final;
+ binder::Status registerLoadingProgressListener(
+ int32_t storageId,
+ const ::android::sp<::android::os::incremental::IStorageLoadingProgressListener>&
+ progressListener,
+ bool* _aidl_return) final;
+ binder::Status unregisterLoadingProgressListener(int32_t storageId, bool* _aidl_return) final;
private:
android::incremental::IncrementalService mImpl;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 447ee55..10a508b 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -275,6 +275,7 @@
mJni(sm.getJni()),
mLooper(sm.getLooper()),
mTimedQueue(sm.getTimedQueue()),
+ mProgressUpdateJobQueue(sm.getProgressUpdateJobQueue()),
mFs(sm.getFs()),
mIncrementalDir(rootDir) {
CHECK(mVold) << "Vold service is unavailable";
@@ -283,6 +284,7 @@
CHECK(mJni) << "JNI is unavailable";
CHECK(mLooper) << "Looper is unavailable";
CHECK(mTimedQueue) << "TimedQueue is unavailable";
+ CHECK(mProgressUpdateJobQueue) << "mProgressUpdateJobQueue is unavailable";
CHECK(mFs) << "Fs is unavailable";
mJobQueue.reserve(16);
@@ -308,6 +310,7 @@
mJobProcessor.join();
mCmdLooperThread.join();
mTimedQueue->stop();
+ mProgressUpdateJobQueue->stop();
// Ensure that mounts are destroyed while the service is still valid.
mBindsByPath.clear();
mMounts.clear();
@@ -1744,6 +1747,35 @@
return (float)filledBlocks / (float)totalBlocks;
}
+bool IncrementalService::updateLoadingProgress(
+ StorageId storage, const StorageLoadingProgressListener& progressListener) {
+ const auto progress = getLoadingProgress(storage);
+ if (progress < 0) {
+ // Failed to get progress from incfs, abort.
+ return false;
+ }
+ progressListener->onStorageLoadingProgressChanged(storage, progress);
+ if (progress > 1 - 0.001f) {
+ // Stop updating progress once it is fully loaded
+ return true;
+ }
+ static constexpr auto kProgressUpdateInterval = 1000ms;
+ addTimedJob(*mProgressUpdateJobQueue, storage, kProgressUpdateInterval /* repeat after 1s */,
+ [storage, progressListener, this]() {
+ updateLoadingProgress(storage, progressListener);
+ });
+ return true;
+}
+
+bool IncrementalService::registerLoadingProgressListener(
+ StorageId storage, const StorageLoadingProgressListener& progressListener) {
+ return updateLoadingProgress(storage, progressListener);
+}
+
+bool IncrementalService::unregisterLoadingProgressListener(StorageId storage) {
+ return removeTimedJobs(*mProgressUpdateJobQueue, storage);
+}
+
bool IncrementalService::perfLoggingEnabled() {
static const bool enabled = base::GetBoolProperty("incremental.perflogging", false);
return enabled;
@@ -1826,18 +1858,21 @@
}
}
-void IncrementalService::addTimedJob(MountId id, Milliseconds after, Job what) {
+bool IncrementalService::addTimedJob(TimedQueueWrapper& timedQueue, MountId id, Milliseconds after,
+ Job what) {
if (id == kInvalidStorageId) {
- return;
+ return false;
}
- mTimedQueue->addJob(id, after, std::move(what));
+ timedQueue.addJob(id, after, std::move(what));
+ return true;
}
-void IncrementalService::removeTimedJobs(MountId id) {
+bool IncrementalService::removeTimedJobs(TimedQueueWrapper& timedQueue, MountId id) {
if (id == kInvalidStorageId) {
- return;
+ return false;
}
- mTimedQueue->removeJobs(id);
+ timedQueue.removeJobs(id);
+ return true;
}
IncrementalService::DataLoaderStub::DataLoaderStub(IncrementalService& service, MountId id,
@@ -1879,7 +1914,7 @@
mHealthPath.clear();
unregisterFromPendingReads();
resetHealthControl();
- mService.removeTimedJobs(mId);
+ mService.removeTimedJobs(*mService.mTimedQueue, mId);
}
requestDestroy();
@@ -2169,7 +2204,8 @@
}
LOG(DEBUG) << id() << ": updateHealthStatus in " << double(checkBackAfter.count()) / 1000.0
<< "secs";
- mService.addTimedJob(id(), checkBackAfter, [this]() { updateHealthStatus(); });
+ mService.addTimedJob(*mService.mTimedQueue, id(), checkBackAfter,
+ [this]() { updateHealthStatus(); });
}
// With kTolerance we are expecting these to execute before the next update.
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 267458d..a49e0f3 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -22,6 +22,7 @@
#include <android/content/pm/IDataLoaderStatusListener.h>
#include <android/os/incremental/BnIncrementalServiceConnector.h>
#include <android/os/incremental/BnStorageHealthListener.h>
+#include <android/os/incremental/BnStorageLoadingProgressListener.h>
#include <android/os/incremental/StorageHealthCheckParams.h>
#include <binder/IAppOpsCallback.h>
#include <utils/String16.h>
@@ -65,6 +66,8 @@
using StorageHealthCheckParams = ::android::os::incremental::StorageHealthCheckParams;
using IStorageHealthListener = ::android::os::incremental::IStorageHealthListener;
using StorageHealthListener = ::android::sp<IStorageHealthListener>;
+using IStorageLoadingProgressListener = ::android::os::incremental::IStorageLoadingProgressListener;
+using StorageLoadingProgressListener = ::android::sp<IStorageLoadingProgressListener>;
class IncrementalService final {
public:
@@ -134,6 +137,9 @@
int isFileFullyLoaded(StorageId storage, const std::string& path) const;
float getLoadingProgress(StorageId storage) const;
+ bool registerLoadingProgressListener(StorageId storage,
+ const StorageLoadingProgressListener& progressListener);
+ bool unregisterLoadingProgressListener(StorageId storage);
RawMetadata getMetadata(StorageId storage, std::string_view path) const;
RawMetadata getMetadata(StorageId storage, FileId node) const;
@@ -354,8 +360,10 @@
void runCmdLooper();
- void addTimedJob(MountId id, Milliseconds after, Job what);
- void removeTimedJobs(MountId id);
+ bool addTimedJob(TimedQueueWrapper& timedQueue, MountId id, Milliseconds after, Job what);
+ bool removeTimedJobs(TimedQueueWrapper& timedQueue, MountId id);
+ bool updateLoadingProgress(int32_t storageId,
+ const StorageLoadingProgressListener& progressListener);
private:
const std::unique_ptr<VoldServiceWrapper> mVold;
@@ -365,6 +373,7 @@
const std::unique_ptr<JniWrapper> mJni;
const std::unique_ptr<LooperWrapper> mLooper;
const std::unique_ptr<TimedQueueWrapper> mTimedQueue;
+ const std::unique_ptr<TimedQueueWrapper> mProgressUpdateJobQueue;
const std::unique_ptr<FsWrapper> mFs;
const std::string mIncrementalDir;
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index f6d89c5..144c466 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -357,6 +357,10 @@
return std::make_unique<RealTimedQueueWrapper>(mJvm);
}
+std::unique_ptr<TimedQueueWrapper> RealServiceManager::getProgressUpdateJobQueue() {
+ return std::make_unique<RealTimedQueueWrapper>(mJvm);
+}
+
std::unique_ptr<FsWrapper> RealServiceManager::getFs() {
return std::make_unique<RealFsWrapper>();
}
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index 6376d86..4815caf 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -154,6 +154,7 @@
virtual std::unique_ptr<JniWrapper> getJni() = 0;
virtual std::unique_ptr<LooperWrapper> getLooper() = 0;
virtual std::unique_ptr<TimedQueueWrapper> getTimedQueue() = 0;
+ virtual std::unique_ptr<TimedQueueWrapper> getProgressUpdateJobQueue() = 0;
virtual std::unique_ptr<FsWrapper> getFs() = 0;
};
@@ -170,6 +171,7 @@
std::unique_ptr<JniWrapper> getJni() final;
std::unique_ptr<LooperWrapper> getLooper() final;
std::unique_ptr<TimedQueueWrapper> getTimedQueue() final;
+ std::unique_ptr<TimedQueueWrapper> getProgressUpdateJobQueue() final;
std::unique_ptr<FsWrapper> getFs() final;
private:
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index a290a17..aec9fa1 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -504,6 +504,14 @@
int32_t mStatus = -1;
};
+class MockStorageLoadingProgressListener : public IStorageLoadingProgressListener {
+public:
+ MockStorageLoadingProgressListener() = default;
+ MOCK_METHOD2(onStorageLoadingProgressChanged,
+ binder::Status(int32_t storageId, float progress));
+ MOCK_METHOD0(onAsBinder, IBinder*());
+};
+
class MockServiceManager : public ServiceManagerWrapper {
public:
MockServiceManager(std::unique_ptr<MockVoldService> vold,
@@ -513,6 +521,7 @@
std::unique_ptr<MockJniWrapper> jni,
std::unique_ptr<MockLooperWrapper> looper,
std::unique_ptr<MockTimedQueueWrapper> timedQueue,
+ std::unique_ptr<MockTimedQueueWrapper> progressUpdateJobQueue,
std::unique_ptr<MockFsWrapper> fs)
: mVold(std::move(vold)),
mDataLoaderManager(std::move(dataLoaderManager)),
@@ -521,6 +530,7 @@
mJni(std::move(jni)),
mLooper(std::move(looper)),
mTimedQueue(std::move(timedQueue)),
+ mProgressUpdateJobQueue(std::move(progressUpdateJobQueue)),
mFs(std::move(fs)) {}
std::unique_ptr<VoldServiceWrapper> getVoldService() final { return std::move(mVold); }
std::unique_ptr<DataLoaderManagerWrapper> getDataLoaderManager() final {
@@ -533,6 +543,9 @@
std::unique_ptr<JniWrapper> getJni() final { return std::move(mJni); }
std::unique_ptr<LooperWrapper> getLooper() final { return std::move(mLooper); }
std::unique_ptr<TimedQueueWrapper> getTimedQueue() final { return std::move(mTimedQueue); }
+ std::unique_ptr<TimedQueueWrapper> getProgressUpdateJobQueue() final {
+ return std::move(mProgressUpdateJobQueue);
+ }
std::unique_ptr<FsWrapper> getFs() final { return std::move(mFs); }
private:
@@ -543,6 +556,7 @@
std::unique_ptr<MockJniWrapper> mJni;
std::unique_ptr<MockLooperWrapper> mLooper;
std::unique_ptr<MockTimedQueueWrapper> mTimedQueue;
+ std::unique_ptr<MockTimedQueueWrapper> mProgressUpdateJobQueue;
std::unique_ptr<MockFsWrapper> mFs;
};
@@ -567,19 +581,19 @@
mLooper = looper.get();
auto timedQueue = std::make_unique<NiceMock<MockTimedQueueWrapper>>();
mTimedQueue = timedQueue.get();
+ auto progressUpdateJobQueue = std::make_unique<NiceMock<MockTimedQueueWrapper>>();
+ mProgressUpdateJobQueue = progressUpdateJobQueue.get();
auto fs = std::make_unique<NiceMock<MockFsWrapper>>();
mFs = fs.get();
- mIncrementalService =
- std::make_unique<IncrementalService>(MockServiceManager(std::move(vold),
- std::move(
- dataloaderManager),
- std::move(incFs),
- std::move(appOps),
- std::move(jni),
- std::move(looper),
- std::move(timedQueue),
- std::move(fs)),
- mRootDir.path);
+ mIncrementalService = std::make_unique<
+ IncrementalService>(MockServiceManager(std::move(vold),
+ std::move(dataloaderManager),
+ std::move(incFs), std::move(appOps),
+ std::move(jni), std::move(looper),
+ std::move(timedQueue),
+ std::move(progressUpdateJobQueue),
+ std::move(fs)),
+ mRootDir.path);
mDataLoaderParcel.packageName = "com.test";
mDataLoaderParcel.arguments = "uri";
mDataLoaderManager->unbindFromDataLoaderSuccess();
@@ -624,6 +638,7 @@
NiceMock<MockJniWrapper>* mJni = nullptr;
NiceMock<MockLooperWrapper>* mLooper = nullptr;
NiceMock<MockTimedQueueWrapper>* mTimedQueue = nullptr;
+ NiceMock<MockTimedQueueWrapper>* mProgressUpdateJobQueue = nullptr;
NiceMock<MockFsWrapper>* mFs = nullptr;
NiceMock<MockDataLoader>* mDataLoader = nullptr;
std::unique_ptr<IncrementalService> mIncrementalService;
@@ -1166,4 +1181,44 @@
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(3);
ASSERT_EQ(0.5, mIncrementalService->getLoadingProgress(storageId));
}
+
+TEST_F(IncrementalServiceTest, testRegisterLoadingProgressListenerSuccess) {
+ mIncFs->countFilledBlocksSuccess();
+ mFs->hasFiles();
+
+ TemporaryDir tempDir;
+ int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+ IncrementalService::CreateOptions::CreateNew,
+ {}, {}, {});
+ sp<NiceMock<MockStorageLoadingProgressListener>> listener{
+ new NiceMock<MockStorageLoadingProgressListener>};
+ NiceMock<MockStorageLoadingProgressListener>* listenerMock = listener.get();
+ EXPECT_CALL(*listenerMock, onStorageLoadingProgressChanged(_, _)).Times(2);
+ EXPECT_CALL(*mProgressUpdateJobQueue, addJob(_, _, _)).Times(2);
+ mIncrementalService->registerLoadingProgressListener(storageId, listener);
+ // Timed callback present.
+ ASSERT_EQ(storageId, mProgressUpdateJobQueue->mId);
+ ASSERT_EQ(mProgressUpdateJobQueue->mAfter, 1000ms);
+ auto timedCallback = mProgressUpdateJobQueue->mWhat;
+ timedCallback();
+ ASSERT_EQ(storageId, mProgressUpdateJobQueue->mId);
+ ASSERT_EQ(mProgressUpdateJobQueue->mAfter, 1000ms);
+ mIncrementalService->unregisterLoadingProgressListener(storageId);
+ ASSERT_EQ(mProgressUpdateJobQueue->mAfter, Milliseconds{});
+}
+
+TEST_F(IncrementalServiceTest, testRegisterLoadingProgressListenerFailsToGetProgress) {
+ mIncFs->countFilledBlocksFails();
+ mFs->hasFiles();
+
+ TemporaryDir tempDir;
+ int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+ IncrementalService::CreateOptions::CreateNew,
+ {}, {}, {});
+ sp<NiceMock<MockStorageLoadingProgressListener>> listener{
+ new NiceMock<MockStorageLoadingProgressListener>};
+ NiceMock<MockStorageLoadingProgressListener>* listenerMock = listener.get();
+ EXPECT_CALL(*listenerMock, onStorageLoadingProgressChanged(_, _)).Times(0);
+ mIncrementalService->registerLoadingProgressListener(storageId, listener);
+}
} // namespace android::os::incremental
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 97ae505..eca9f15 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -150,6 +150,7 @@
import com.android.server.power.PowerManagerService;
import com.android.server.power.ShutdownThread;
import com.android.server.power.ThermalManagerService;
+import com.android.server.powerstats.PowerStatsService;
import com.android.server.profcollect.ProfcollectForwardingService;
import com.android.server.recoverysystem.RecoverySystemService;
import com.android.server.restrictions.RestrictionsManagerService;
@@ -292,6 +293,8 @@
"com.android.server.timedetector.TimeDetectorService$Lifecycle";
private static final String TIME_ZONE_DETECTOR_SERVICE_CLASS =
"com.android.server.timezonedetector.TimeZoneDetectorService$Lifecycle";
+ private static final String LOCATION_TIME_ZONE_MANAGER_SERVICE_CLASS =
+ "com.android.server.location.timezone.LocationTimeZoneManagerService$Lifecycle";
private static final String ACCESSIBILITY_MANAGER_SERVICE_CLASS =
"com.android.server.accessibility.AccessibilityManagerService$Lifecycle";
private static final String ADB_SERVICE_CLASS =
@@ -761,6 +764,11 @@
mSystemServiceManager.startService(UriGrantsManagerService.Lifecycle.class);
t.traceEnd();
+ t.traceBegin("StartPowerStatsService");
+ // Tracks rail data to be used for power statistics.
+ mSystemServiceManager.startService(PowerStatsService.class);
+ t.traceEnd();
+
// Activity manager runs the show.
t.traceBegin("StartActivityManager");
// TODO: Might need to move after migration to WM.
@@ -1615,7 +1623,7 @@
try {
mSystemServiceManager.startService(TIME_DETECTOR_SERVICE_CLASS);
} catch (Throwable e) {
- reportWtf("starting StartTimeDetectorService service", e);
+ reportWtf("starting TimeDetectorService service", e);
}
t.traceEnd();
@@ -1623,7 +1631,15 @@
try {
mSystemServiceManager.startService(TIME_ZONE_DETECTOR_SERVICE_CLASS);
} catch (Throwable e) {
- reportWtf("starting StartTimeZoneDetectorService service", e);
+ reportWtf("starting TimeZoneDetectorService service", e);
+ }
+ t.traceEnd();
+
+ t.traceBegin("StartLocationTimeZoneManagerService");
+ try {
+ mSystemServiceManager.startService(LOCATION_TIME_ZONE_MANAGER_SERVICE_CLASS);
+ } catch (Throwable e) {
+ reportWtf("starting LocationTimeZoneManagerService service", e);
}
t.traceEnd();
diff --git a/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java b/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java
index 7672cd0..c03a5a7 100644
--- a/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java
+++ b/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java
@@ -129,7 +129,7 @@
if (files == null || files.length == 0) {
return null;
} else if (files.length > 1) {
- // This can't possibly happen, but sanity check.
+ // This can't possibly happen, but validity check.
Slog.w(TAG, "Found multiple files with the same name: " + Arrays.toString(files));
}
return parseFile(files[0]);
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 0789d68..120182f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -1081,6 +1081,49 @@
}
}
+ @Test
+ public void nonWakeupAlarmsDeferred() throws Exception {
+ final int numAlarms = 10;
+ final PendingIntent[] pis = new PendingIntent[numAlarms];
+ for (int i = 0; i < numAlarms; i++) {
+ pis[i] = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 5, pis[i]);
+ }
+ doReturn(true).when(mService).checkAllowNonWakeupDelayLocked(anyLong());
+ // Advance time past all expirations.
+ mNowElapsedTest += numAlarms + 5;
+ mTestTimer.expire();
+ assertEquals(numAlarms, mService.mPendingNonWakeupAlarms.size());
+
+ // These alarms should be sent on interactive state change to true
+ mService.interactiveStateChangedLocked(false);
+ mService.interactiveStateChangedLocked(true);
+
+ for (int i = 0; i < numAlarms; i++) {
+ verify(pis[i]).send(eq(mMockContext), eq(0), any(Intent.class), any(),
+ any(Handler.class), isNull(), any());
+ }
+ }
+
+ @Test
+ public void alarmCountOnPendingNonWakeupAlarmsRemoved() throws Exception {
+ final int numAlarms = 10;
+ final PendingIntent[] pis = new PendingIntent[numAlarms];
+ for (int i = 0; i < numAlarms; i++) {
+ pis[i] = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 5, pis[i]);
+ }
+ doReturn(true).when(mService).checkAllowNonWakeupDelayLocked(anyLong());
+ // Advance time past all expirations.
+ mNowElapsedTest += numAlarms + 5;
+ mTestTimer.expire();
+ assertEquals(numAlarms, mService.mPendingNonWakeupAlarms.size());
+ for (int i = 0; i < numAlarms; i++) {
+ mService.removeLocked(pis[i], null);
+ assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
+ }
+ }
+
@After
public void tearDown() {
if (mMockingSession != null) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
new file mode 100644
index 0000000..04e8b63
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
@@ -0,0 +1,258 @@
+/*
+ * 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.am;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+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.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
+import com.android.server.am.ActivityManagerService.Injector;
+import com.android.server.appop.AppOpsService;
+import com.android.server.wm.ActivityTaskManagerService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.quality.Strictness;
+
+import java.io.File;
+
+@Presubmit
+public class AppChildProcessTest {
+ private static final String TAG = AppChildProcessTest.class.getSimpleName();
+
+ @Rule public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule();
+ @Mock private AppOpsService mAppOpsService;
+ @Mock private PackageManagerInternal mPackageManagerInt;
+ private StaticMockitoSession mMockitoSession;
+
+ private Context mContext = getInstrumentation().getTargetContext();
+ private TestInjector mInjector;
+ private ActivityManagerService mAms;
+ private ProcessList mProcessList;
+ private PhantomProcessList mPhantomProcessList;
+ private Handler mHandler;
+ private HandlerThread mHandlerThread;
+
+ @BeforeClass
+ public static void setUpOnce() {
+ System.setProperty("dexmaker.share_classloader", "true");
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mMockitoSession = mockitoSession()
+ .spyStatic(Process.class)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ final ProcessList pList = new ProcessList();
+ mProcessList = spy(pList);
+
+ mInjector = new TestInjector(mContext);
+ mAms = new ActivityManagerService(mInjector, mServiceThreadRule.getThread());
+ mAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
+ mAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
+ mAms.mAtmInternal = spy(mAms.mActivityTaskManager.getAtmInternal());
+ mAms.mPackageManagerInt = mPackageManagerInt;
+ pList.mService = mAms;
+ mPhantomProcessList = mAms.mPhantomProcessList;
+ doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
+ doReturn(false).when(() -> Process.supportsPidFd());
+ // Remove stale instance of PackageManagerInternal if there is any
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ mMockitoSession.finishMocking();
+ mHandlerThread.quit();
+ }
+
+ @Test
+ public void testManageAppChildProcesses() throws Exception {
+ final int initPid = 1;
+ final int rootUid = 0;
+ final int zygote64Pid = 100;
+ final int zygote32Pid = 101;
+ final int app1Pid = 200;
+ final int app2Pid = 201;
+ final int app1Uid = 10000;
+ final int app2Uid = 10001;
+ final int child1Pid = 300;
+ final int child2Pid = 301;
+ final int nativePid = 400;
+ final String zygote64ProcessName = "zygote64";
+ final String zygote32ProcessName = "zygote32";
+ final String app1ProcessName = "test1";
+ final String app2ProcessName = "test2";
+ final String child1ProcessName = "test1_child1";
+ final String child2ProcessName = "test1_child1_child2";
+ final String nativeProcessName = "test_native";
+
+ makeParent(zygote64Pid, initPid);
+ makeParent(zygote32Pid, initPid);
+
+ makeAppProcess(app1Pid, app1Uid, app1ProcessName, app1ProcessName);
+ makeParent(app1Pid, zygote64Pid);
+ makeAppProcess(app2Pid, app2Uid, app2ProcessName, app2ProcessName);
+ makeParent(app2Pid, zygote64Pid);
+
+ assertEquals(0, mPhantomProcessList.mPhantomProcesses.size());
+
+ // Verify zygote itself isn't a phantom process
+ assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
+ zygote64ProcessName, rootUid, zygote64Pid));
+ assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
+ zygote32ProcessName, rootUid, zygote32Pid));
+ // Verify none of the app isn't a phantom process
+ assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
+ app1ProcessName, app1Uid, app1Pid));
+ assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
+ app2ProcessName, app2Uid, app2Pid));
+
+ // "Fork" an app child process
+ makeParent(child1Pid, app1Pid);
+ PhantomProcessRecord pr = mPhantomProcessList
+ .getOrCreatePhantomProcessIfNeededLocked(child1ProcessName, app1Uid, child1Pid);
+ assertTrue(pr != null);
+ assertEquals(1, mPhantomProcessList.mPhantomProcesses.size());
+ assertEquals(pr, mPhantomProcessList.mPhantomProcesses.valueAt(0));
+ verifyPhantomProcessRecord(pr, child1ProcessName, app1Uid, child1Pid);
+
+ // Create another native process from init
+ makeParent(nativePid, initPid);
+ assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
+ nativeProcessName, rootUid, nativePid));
+ assertEquals(1, mPhantomProcessList.mPhantomProcesses.size());
+ assertEquals(pr, mPhantomProcessList.mPhantomProcesses.valueAt(0));
+
+ // "Fork" another app child process
+ makeParent(child2Pid, child1Pid);
+ PhantomProcessRecord pr2 = mPhantomProcessList
+ .getOrCreatePhantomProcessIfNeededLocked(child2ProcessName, app1Uid, child2Pid);
+ assertTrue(pr2 != null);
+ assertEquals(2, mPhantomProcessList.mPhantomProcesses.size());
+ verifyPhantomProcessRecord(pr2, child2ProcessName, app1Uid, child2Pid);
+
+ ArraySet<PhantomProcessRecord> set = new ArraySet<>();
+ set.add(pr);
+ set.add(pr2);
+ for (int i = mPhantomProcessList.mPhantomProcesses.size() - 1; i >= 0; i--) {
+ set.remove(mPhantomProcessList.mPhantomProcesses.valueAt(i));
+ }
+ assertEquals(0, set.size());
+ }
+
+ private void verifyPhantomProcessRecord(PhantomProcessRecord pr,
+ String processName, int uid, int pid) {
+ assertEquals(processName, pr.mProcessName);
+ assertEquals(uid, pr.mUid);
+ assertEquals(pid, pr.mPid);
+ }
+
+ private void makeAppProcess(int pid, int uid, String packageName, String processName) {
+ ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = packageName;
+ ProcessRecord app = new ProcessRecord(mAms, ai, processName, uid);
+ app.pid = pid;
+ mAms.mPidsSelfLocked.doAddInternal(app);
+ }
+
+ private void makeParent(int pid, int ppid) {
+ doReturn(ppid).when(() -> Process.getParentPid(eq(pid)));
+ }
+
+ private class TestInjector extends Injector {
+ TestInjector(Context context) {
+ super(context);
+ }
+
+ @Override
+ public AppOpsService getAppOpsService(File file, Handler handler) {
+ return mAppOpsService;
+ }
+
+ @Override
+ public Handler getUiHandler(ActivityManagerService service) {
+ return mHandler;
+ }
+
+ @Override
+ public ProcessList getProcessList(ActivityManagerService service) {
+ return mProcessList;
+ }
+ }
+
+ static class ServiceThreadRule implements TestRule {
+ private ServiceThread mThread;
+
+ ServiceThread getThread() {
+ return mThread;
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ mThread = new ServiceThread("TestServiceThread",
+ Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */);
+ mThread.start();
+ try {
+ base.evaluate();
+ } finally {
+ mThread.getThreadHandler().runWithScissors(mThread::quit, 0 /* timeout */);
+ }
+ }
+ };
+ }
+ }
+
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index 8db09b4..e9a50b3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -120,6 +120,7 @@
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
mProcessList = spy(new ProcessList());
+ ProcessList.sKillHandler = null;
mAppExitInfoTracker = spy(new AppExitInfoTracker());
setFieldValue(AppExitInfoTracker.class, mAppExitInfoTracker, "mIsolatedUidRecords",
spy(mAppExitInfoTracker.new IsolatedUidRecords()));
@@ -147,6 +148,7 @@
public void tearDown() {
LocalServices.removeServiceForTest(PackageManagerInternal.class);
mHandlerThread.quit();
+ ProcessList.sKillHandler = null;
}
private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index e724e60..7f86faa 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -364,7 +364,7 @@
.setAppIdleWhitelist(eq(UID_BLUE), anyBoolean());
assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
- // Whitelisting doesn't need to be requested again.
+ // Allowlisting doesn't need to be requested again.
controller.requestStandbyExceptionLocked(red);
inOrder.verify(mNetPolicyManagerInternal, never())
.setAppIdleWhitelist(eq(UID_RED), anyBoolean());
@@ -434,7 +434,7 @@
.setAppIdleWhitelist(eq(UID_BLUE), anyBoolean());
assertTrue(controller.isStandbyExceptionRequestedLocked(UID_RED));
assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
- // Whitelisting doesn't need to be requested again.
+ // Allowlisting doesn't need to be requested again.
controller.evaluateStateLocked(red);
inOrder.verify(mNetPolicyManagerInternal, never())
.setAppIdleWhitelist(eq(UID_RED), anyBoolean());
@@ -473,7 +473,7 @@
assertFalse(controller.isStandbyExceptionRequestedLocked(UID_RED));
assertFalse(controller.isStandbyExceptionRequestedLocked(UID_BLUE));
- // Test that a currently whitelisted uid is now removed.
+ // Test that a currently allowlisted uid is now removed.
controller.requestStandbyExceptionLocked(blue);
inOrder.verify(mNetPolicyManagerInternal, times(1))
.setAppIdleWhitelist(eq(UID_BLUE), eq(true));
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 1bf9c2a..6d40034 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -46,6 +46,7 @@
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.SystemClock;
import android.provider.MediaStore;
@@ -685,6 +686,8 @@
}
private static JobStatus createJobStatus(JobInfo job) {
- return JobStatus.createFromJobInfo(job, 0, null, -1, "JobStatusTest");
+ JobStatus jobStatus = JobStatus.createFromJobInfo(job, 0, null, -1, "JobStatusTest");
+ jobStatus.serviceInfo = mock(ServiceInfo.class);
+ return jobStatus;
}
}
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 d8874e4..18bd6b1 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
@@ -61,6 +61,7 @@
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ServiceInfo;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
import android.os.Handler;
@@ -299,10 +300,11 @@
JobInfo jobInfo) {
JobStatus js = JobStatus.createFromJobInfo(
jobInfo, callingUid, packageName, SOURCE_USER_ID, testTag);
+ js.serviceInfo = mock(ServiceInfo.class);
// Make sure tests aren't passing just because the default bucket is likely ACTIVE.
js.setStandbyBucket(FREQUENT_INDEX);
// Make sure Doze and background-not-restricted don't affect tests.
- js.setDeviceNotDozingConstraintSatisfied(/* state */ true, /* whitelisted */false);
+ js.setDeviceNotDozingConstraintSatisfied(/* state */ true, /* allowlisted */false);
js.setBackgroundNotRestrictedConstraintSatisfied(true);
return js;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java
index f2246da..a0f48c6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java
@@ -148,7 +148,7 @@
// accurate the coarsened average will be. we use 70% as a lower bound by -very- roughly
// taking the area within a grid where we expect a reasonable percentage of points generated
// by step() to fall in another grid square. this likely doesn't have much mathematical
- // validity, but it serves as a sanity test as least.
+ // validity, but it serves as a validity test as least.
assertThat(passed / (double) iterations).isGreaterThan(.70);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
index 80ad0a8..1491486 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
@@ -24,7 +24,6 @@
import static android.location.Criteria.ACCURACY_COARSE;
import static android.location.Criteria.ACCURACY_FINE;
import static android.location.Criteria.POWER_HIGH;
-import static android.location.LocationManager.PASSIVE_PROVIDER;
import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
import static androidx.test.ext.truth.location.LocationSubject.assertThat;
@@ -356,8 +355,7 @@
@Test
public void testPassive_Listener() throws Exception {
ILocationListener listener = createMockLocationListener();
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(PASSIVE_PROVIDER, 0,
- 0, false);
+ LocationRequest request = new LocationRequest.Builder(0).build();
mPassive.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
Location loc = createLocation(NAME, mRandom);
@@ -382,8 +380,7 @@
ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class);
ILocationListener listener = createMockLocationListener();
- mManager.registerLocationRequest(
- LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), IDENTITY,
+ mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), IDENTITY,
PERMISSION_FINE, listener);
Location loc = createLocation(NAME, mRandom);
@@ -437,8 +434,7 @@
"attribution");
ILocationListener listener = createMockLocationListener();
- mManager.registerLocationRequest(
- LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), identity,
+ mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), identity,
PERMISSION_FINE, listener);
Location loc = createLocation(NAME, mRandom);
@@ -451,8 +447,7 @@
@Test
public void testRegisterListener_Unregister() throws Exception {
ILocationListener listener = createMockLocationListener();
- mManager.registerLocationRequest(
- LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), IDENTITY,
+ mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), IDENTITY,
PERMISSION_FINE, listener);
mManager.unregisterLocationRequest(listener);
@@ -470,8 +465,7 @@
"attribution");
ILocationListener listener = createMockLocationListener();
- mManager.registerLocationRequest(
- LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), identity,
+ mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), identity,
PERMISSION_FINE, listener);
CountDownLatch blocker = new CountDownLatch(1);
@@ -493,8 +487,7 @@
@Test
public void testRegisterListener_NumUpdates() throws Exception {
ILocationListener listener = createMockLocationListener();
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
- false).setNumUpdates(5);
+ LocationRequest request = new LocationRequest.Builder(0).setMaxUpdates(5).build();
mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
@@ -511,8 +504,7 @@
@Test
public void testRegisterListener_ExpiringAlarm() throws Exception {
ILocationListener listener = createMockLocationListener();
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
- false).setExpireIn(5000);
+ LocationRequest request = new LocationRequest.Builder(0).setDurationMillis(5000).build();
mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
long baseTimeMs = SystemClock.elapsedRealtime();
@@ -535,8 +527,7 @@
@Test
public void testRegisterListener_ExpiringNoAlarm() throws Exception {
ILocationListener listener = createMockLocationListener();
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
- false).setExpireIn(25);
+ LocationRequest request = new LocationRequest.Builder(0).setDurationMillis(25).build();
mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
Thread.sleep(25);
@@ -547,22 +538,10 @@
}
@Test
- public void testRegisterListener_AlreadyExpired() throws Exception {
- ILocationListener listener = createMockLocationListener();
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
- false).setExpireIn(-1);
- mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
-
- mProvider.setProviderLocation(createLocation(NAME, mRandom));
- verify(listener, never()).onLocationChanged(any(Location.class),
- nullable(IRemoteCallback.class));
- }
-
- @Test
public void testRegisterListener_FastestInterval() throws Exception {
ILocationListener listener = createMockLocationListener();
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 5000, 0,
- false).setFastestInterval(5000);
+ LocationRequest request = new LocationRequest.Builder(5000).setMinUpdateIntervalMillis(
+ 5000).build();
mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
@@ -575,8 +554,8 @@
@Test
public void testRegisterListener_SmallestDisplacement() throws Exception {
ILocationListener listener = createMockLocationListener();
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 5000, 0,
- false).setSmallestDisplacement(1f);
+ LocationRequest request = new LocationRequest.Builder(5000).setMinUpdateDistanceMeters(
+ 1f).build();
mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
Location loc = createLocation(NAME, mRandom);
@@ -590,7 +569,7 @@
@Test
public void testRegisterListener_NoteOpFailure() throws Exception {
ILocationListener listener = createMockLocationListener();
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false);
+ LocationRequest request = new LocationRequest.Builder(0).build();
mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
mInjector.getAppOpsHelper().setAppOpAllowed(OP_FINE_LOCATION, IDENTITY.getPackageName(),
@@ -608,8 +587,7 @@
"attribution");
ILocationListener listener = createMockLocationListener();
- mManager.registerLocationRequest(
- LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false), identity,
+ mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), identity,
PERMISSION_FINE, listener);
CountDownLatch blocker = new CountDownLatch(1);
@@ -637,8 +615,7 @@
ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class);
ILocationCallback listener = createMockGetCurrentLocationListener();
- LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
- false);
+ LocationRequest locationRequest = new LocationRequest.Builder(0).build();
ICancellationSignal cancellationSignal = CancellationSignal.createTransport();
mManager.getCurrentLocation(locationRequest, IDENTITY,
PERMISSION_FINE, cancellationSignal, listener);
@@ -654,8 +631,7 @@
@Test
public void testGetCurrentLocation_Cancel() throws Exception {
ILocationCallback listener = createMockGetCurrentLocationListener();
- LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
- false);
+ LocationRequest locationRequest = new LocationRequest.Builder(0).build();
ICancellationSignal cancellationSignal = CancellationSignal.createTransport();
mManager.getCurrentLocation(locationRequest, IDENTITY,
PERMISSION_FINE, cancellationSignal, listener);
@@ -669,8 +645,7 @@
@Test
public void testGetCurrentLocation_ProviderDisabled() throws Exception {
ILocationCallback listener = createMockGetCurrentLocationListener();
- LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
- false);
+ LocationRequest locationRequest = new LocationRequest.Builder(0).build();
ICancellationSignal cancellationSignal = CancellationSignal.createTransport();
mManager.getCurrentLocation(locationRequest, IDENTITY,
PERMISSION_FINE, cancellationSignal, listener);
@@ -686,8 +661,7 @@
mProvider.setProviderAllowed(false);
ILocationCallback listener = createMockGetCurrentLocationListener();
- LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
- false);
+ LocationRequest locationRequest = new LocationRequest.Builder(0).build();
ICancellationSignal cancellationSignal = CancellationSignal.createTransport();
mManager.getCurrentLocation(locationRequest, IDENTITY,
PERMISSION_FINE, cancellationSignal, listener);
@@ -705,8 +679,7 @@
mProvider.setProviderLocation(loc);
ILocationCallback listener = createMockGetCurrentLocationListener();
- LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
- false);
+ LocationRequest locationRequest = new LocationRequest.Builder(0).build();
ICancellationSignal cancellationSignal = CancellationSignal.createTransport();
mManager.getCurrentLocation(locationRequest, IDENTITY,
PERMISSION_FINE, cancellationSignal, listener);
@@ -718,8 +691,7 @@
@Test
public void testGetCurrentLocation_Timeout() throws Exception {
ILocationCallback listener = createMockGetCurrentLocationListener();
- LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
- false);
+ LocationRequest locationRequest = new LocationRequest.Builder(0).build();
ICancellationSignal cancellationSignal = CancellationSignal.createTransport();
mManager.getCurrentLocation(locationRequest, IDENTITY,
PERMISSION_FINE, cancellationSignal, listener);
@@ -742,7 +714,7 @@
IDENTITY.getPackageName())).isFalse();
ILocationListener listener = createMockLocationListener();
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false);
+ LocationRequest request = new LocationRequest.Builder(0).build();
mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
@@ -767,57 +739,57 @@
@Test
public void testProviderRequest() {
- assertThat(mProvider.getRequest().reportLocation).isFalse();
- assertThat(mProvider.getRequest().locationRequests).isEmpty();
+ assertThat(mProvider.getRequest().isActive()).isFalse();
+ assertThat(mProvider.getRequest().getLocationRequests()).isEmpty();
ILocationListener listener1 = createMockLocationListener();
- LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0, false);
+ LocationRequest request1 = new LocationRequest.Builder(5).build();
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
- assertThat(mProvider.getRequest().reportLocation).isTrue();
- assertThat(mProvider.getRequest().locationRequests).containsExactly(request1);
- assertThat(mProvider.getRequest().locationSettingsIgnored).isFalse();
- assertThat(mProvider.getRequest().interval).isEqualTo(5);
- assertThat(mProvider.getRequest().lowPowerMode).isFalse();
- assertThat(mProvider.getRequest().workSource).isNotNull();
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mProvider.getRequest().getLocationRequests()).containsExactly(request1);
+ assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse();
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
+ assertThat(mProvider.getRequest().isLowPower()).isFalse();
+ assertThat(mProvider.getRequest().getWorkSource()).isNotNull();
ILocationListener listener2 = createMockLocationListener();
- LocationRequest request2 = LocationRequest.createFromDeprecatedProvider(NAME, 1, 0,
- false).setLowPowerMode(true);
+ LocationRequest request2 = new LocationRequest.Builder(1).setLowPower(true).build();
mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
- assertThat(mProvider.getRequest().reportLocation).isTrue();
- assertThat(mProvider.getRequest().locationRequests).containsExactly(request1, request2);
- assertThat(mProvider.getRequest().locationSettingsIgnored).isFalse();
- assertThat(mProvider.getRequest().interval).isEqualTo(1);
- assertThat(mProvider.getRequest().lowPowerMode).isFalse();
- assertThat(mProvider.getRequest().workSource).isNotNull();
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mProvider.getRequest().getLocationRequests()).containsExactly(request1,
+ request2);
+ assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse();
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1);
+ assertThat(mProvider.getRequest().isLowPower()).isFalse();
+ assertThat(mProvider.getRequest().getWorkSource()).isNotNull();
mManager.unregisterLocationRequest(listener1);
- assertThat(mProvider.getRequest().reportLocation).isTrue();
- assertThat(mProvider.getRequest().locationRequests).containsExactly(request2);
- assertThat(mProvider.getRequest().locationSettingsIgnored).isFalse();
- assertThat(mProvider.getRequest().interval).isEqualTo(1);
- assertThat(mProvider.getRequest().lowPowerMode).isTrue();
- assertThat(mProvider.getRequest().workSource).isNotNull();
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mProvider.getRequest().getLocationRequests()).containsExactly(request2);
+ assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse();
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1);
+ assertThat(mProvider.getRequest().isLowPower()).isTrue();
+ assertThat(mProvider.getRequest().getWorkSource()).isNotNull();
mManager.unregisterLocationRequest(listener2);
- assertThat(mProvider.getRequest().reportLocation).isFalse();
- assertThat(mProvider.getRequest().locationRequests).isEmpty();
+ assertThat(mProvider.getRequest().isActive()).isFalse();
+ assertThat(mProvider.getRequest().getLocationRequests()).isEmpty();
}
@Test
public void testProviderRequest_BackgroundThrottle() {
ILocationListener listener1 = createMockLocationListener();
- LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0, false);
+ LocationRequest request1 = new LocationRequest.Builder(5).build();
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
- assertThat(mProvider.getRequest().interval).isEqualTo(5);
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
mInjector.getAppForegroundHelper().setAppForeground(IDENTITY.getUid(), false);
- assertThat(mProvider.getRequest().interval).isEqualTo(
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(
mInjector.getSettingsHelper().getBackgroundThrottleIntervalMs());
}
@@ -827,21 +799,21 @@
Collections.singleton(IDENTITY.getPackageName()));
ILocationListener listener1 = createMockLocationListener();
- LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0, false);
+ LocationRequest request1 = new LocationRequest.Builder(5).build();
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
- assertThat(mProvider.getRequest().reportLocation).isTrue();
- assertThat(mProvider.getRequest().interval).isEqualTo(5);
- assertThat(mProvider.getRequest().locationSettingsIgnored).isFalse();
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
+ assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse();
ILocationListener listener2 = createMockLocationListener();
- LocationRequest request2 = LocationRequest.createFromDeprecatedProvider(NAME, 1, 0,
- false).setLocationSettingsIgnored(true);
+ LocationRequest request2 = new LocationRequest.Builder(1).setLocationSettingsIgnored(
+ true).build();
mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
- assertThat(mProvider.getRequest().reportLocation).isTrue();
- assertThat(mProvider.getRequest().interval).isEqualTo(1);
- assertThat(mProvider.getRequest().locationSettingsIgnored).isTrue();
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1);
+ assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isTrue();
}
@Test
@@ -850,20 +822,20 @@
Collections.singleton(IDENTITY.getPackageName()));
ILocationListener listener1 = createMockLocationListener();
- LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 1, 0, false);
+ LocationRequest request1 = new LocationRequest.Builder(1).build();
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
ILocationListener listener2 = createMockLocationListener();
- LocationRequest request2 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0,
- false).setLocationSettingsIgnored(true);
+ LocationRequest request2 = new LocationRequest.Builder(5).setLocationSettingsIgnored(
+ true).build();
mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId());
- assertThat(mProvider.getRequest().reportLocation).isTrue();
- assertThat(mProvider.getRequest().locationRequests).containsExactly(request2);
- assertThat(mProvider.getRequest().interval).isEqualTo(5);
- assertThat(mProvider.getRequest().locationSettingsIgnored).isTrue();
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mProvider.getRequest().getLocationRequests()).containsExactly(request2);
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
+ assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isTrue();
}
@Test
@@ -872,15 +844,15 @@
Collections.singleton(IDENTITY.getPackageName()));
ILocationListener listener = createMockLocationListener();
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 1, 0,
- false).setLocationSettingsIgnored(true);
+ LocationRequest request = new LocationRequest.Builder(1).setLocationSettingsIgnored(
+ true).build();
mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist(Collections.emptySet());
- assertThat(mProvider.getRequest().reportLocation).isTrue();
- assertThat(mProvider.getRequest().interval).isEqualTo(1);
- assertThat(mProvider.getRequest().locationSettingsIgnored).isFalse();
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1);
+ assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse();
}
@Test
@@ -889,14 +861,14 @@
Collections.singleton(IDENTITY.getPackageName()));
ILocationListener listener1 = createMockLocationListener();
- LocationRequest request1 = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0,
- false).setLocationSettingsIgnored(true);
+ LocationRequest request1 = new LocationRequest.Builder(5).setLocationSettingsIgnored(
+ true).build();
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
- assertThat(mProvider.getRequest().interval).isEqualTo(5);
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
mInjector.getAppForegroundHelper().setAppForeground(IDENTITY.getUid(), false);
- assertThat(mProvider.getRequest().interval).isEqualTo(5);
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
}
@Test
@@ -905,13 +877,13 @@
LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
ILocationListener listener = createMockLocationListener();
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 5, 0, false);
+ LocationRequest request = new LocationRequest.Builder(5).build();
mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
- assertThat(mProvider.getRequest().reportLocation).isTrue();
+ assertThat(mProvider.getRequest().isActive()).isTrue();
mInjector.getScreenInteractiveHelper().setScreenInteractive(false);
- assertThat(mProvider.getRequest().reportLocation).isFalse();
+ assertThat(mProvider.getRequest().isActive()).isFalse();
}
private ILocationListener createMockLocationListener() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/MockableLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/MockableLocationProviderTest.java
index 2026363..374fc77 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/MockableLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/MockableLocationProviderTest.java
@@ -95,7 +95,7 @@
assertThat(mProvider.getCurrentRequest()).isEqualTo(EMPTY_REQUEST);
verify(mRealProvider, times(1)).onSetRequest(EMPTY_REQUEST);
- ProviderRequest request = new ProviderRequest.Builder().setInterval(1).build();
+ ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(1).build();
mProvider.setRequest(request);
assertThat(mProvider.getCurrentRequest()).isEqualTo(request);
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 69a9f44..29d3f29 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,6 +34,7 @@
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;
@@ -43,7 +44,6 @@
import java.util.Collection;
import java.util.function.Consumer;
-import java.util.function.Predicate;
@SuppressWarnings("unchecked")
@Presubmit
@@ -319,7 +319,8 @@
}
private static class TestListenerRegistration extends
- ListenerRegistration<Integer, Consumer<TestListenerRegistration>> {
+ RequestListenerRegistration<Integer, Consumer<TestListenerRegistration>,
+ ListenerOperation<Consumer<TestListenerRegistration>>> {
boolean mActive = true;
@@ -330,8 +331,10 @@
}
private static class TestMultiplexer extends
- ListenerMultiplexer<Consumer<TestListenerRegistration>, Integer,
- Consumer<TestListenerRegistration>, TestListenerRegistration, Integer> {
+ ListenerMultiplexer<Consumer<TestListenerRegistration>,
+ Consumer<TestListenerRegistration>,
+ ListenerOperation<Consumer<TestListenerRegistration>>, TestListenerRegistration,
+ Integer> {
boolean mRegistered;
int mMergedRequest;
@@ -342,6 +345,11 @@
mCallbacks = callbacks;
}
+ @Override
+ public String getTag() {
+ return "TestMultiplexer";
+ }
+
public void addListener(Integer request, Consumer<TestListenerRegistration> consumer) {
addRegistration(consumer, new TestListenerRegistration(request, consumer));
}
@@ -355,10 +363,6 @@
removeRegistration(consumer, registration);
}
- public void removeListenerIf(Predicate<Consumer<TestListenerRegistration>> predicate) {
- removeRegistrationIf(predicate);
- }
-
public void setActive(Integer request, boolean active) {
updateRegistrations(testRegistration -> {
if (testRegistration.getRequest().equals(request)) {
@@ -374,7 +378,8 @@
}
@Override
- protected boolean registerWithService(Integer mergedRequest) {
+ protected boolean registerWithService(Integer mergedRequest,
+ Collection<TestListenerRegistration> registrations) {
mRegistered = true;
mMergedRequest = mergedRequest;
return true;
@@ -423,7 +428,8 @@
}
@Override
- protected Integer mergeRequests(Collection<TestListenerRegistration> testRegistrations) {
+ protected Integer mergeRegistrations(
+ Collection<TestListenerRegistration> testRegistrations) {
int max = Integer.MIN_VALUE;
for (TestListenerRegistration registration : testRegistrations) {
if (registration.getRequest() > max) {
diff --git a/services/tests/servicestests/apks/install_uses_sdk/res/values/strings.xml b/services/tests/servicestests/apks/install_uses_sdk/res/values/strings.xml
index 3b8b3b1..fcab92b 100644
--- a/services/tests/servicestests/apks/install_uses_sdk/res/values/strings.xml
+++ b/services/tests/servicestests/apks/install_uses_sdk/res/values/strings.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Just need this dummy file to have something to build. -->
+<!-- Just need this placeholder file to have something to build. -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="dummy">dummy</string>
</resources>
diff --git a/services/tests/servicestests/src/com/android/server/CertBlacklisterTest.java b/services/tests/servicestests/src/com/android/server/CertBlacklisterTest.java
index 10b9e7c..90df786 100644
--- a/services/tests/servicestests/src/com/android/server/CertBlacklisterTest.java
+++ b/services/tests/servicestests/src/com/android/server/CertBlacklisterTest.java
@@ -50,14 +50,14 @@
public void testClearBlacklistPubkey() throws Exception {
// clear the gservices setting for a clean slate
overrideSettings(PUBKEY_KEY, "");
- // read the contents of the pubkey blacklist
+ // read the contents of the pubkey denylist
String blacklist = IoUtils.readFileAsString(PUBKEY_PATH);
// Verify that it's empty
assertEquals("", blacklist);
}
public void testSetBlacklistPubkey() throws Exception {
- // build a new thing to blacklist
+ // build a new thing to denylist
String badPubkey = "7ccabd7db47e94a5759901b6a7dfd45d1c091ccc";
// add the gservices override
overrideSettings(PUBKEY_KEY, badPubkey);
@@ -110,14 +110,14 @@
public void testClearBlacklistSerial() throws Exception {
// clear the gservices setting for a clean slate
overrideSettings(SERIAL_KEY, "");
- // read the contents of the pubkey blacklist
+ // read the contents of the pubkey denylist
String blacklist = IoUtils.readFileAsString(SERIAL_PATH);
// Verify that it's empty
assertEquals("", blacklist);
}
public void testSetBlacklistSerial() throws Exception {
- // build a new thing to blacklist
+ // build a new thing to denylist
String badSerial = "22e514121e61c643b1e9b06bd4b9f7d0";
// add the gservices override
overrideSettings(SERIAL_KEY, badSerial);
diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java
index ebbebcb..33ea1d6 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java
@@ -81,14 +81,14 @@
mNmi.isNetworkRestrictedForUid(TEST_UID));
mInjector.reset();
- // Data saver is on and uid is not whitelisted
+ // Data saver is on and uid is not allowlisted
mInjector.setDataSaverMode(true);
mInjector.setUidOnMeteredNetworkList(false, TEST_UID, false);
assertTrue("Should be true since data saver is on and the uid is not whitelisted",
mNmi.isNetworkRestrictedForUid(TEST_UID));
mInjector.reset();
- // Data saver is on and uid is whitelisted
+ // Data saver is on and uid is allowlisted
mInjector.setDataSaverMode(true);
mInjector.setUidOnMeteredNetworkList(false, TEST_UID, true);
assertFalse("Should be false since data saver is on and the uid is whitelisted",
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 23a37c1..b7355ce 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -230,4 +230,19 @@
verify(mMockWindowMagnificationMgr, never()).showMagnificationButton(anyInt(),
anyInt());
}
+
+ @SmallTest
+ public void testOnMagnificationTransitionFailed_capabilitiesIsAll_fallBackToPreviousMode() {
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ userState.setMagnificationCapabilitiesLocked(
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+ userState.setMagnificationModeLocked(
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+
+ mA11yms.onMagnificationTransitionEndedLocked(false);
+
+ assertEquals(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
+ userState.getMagnificationModeLocked());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java
new file mode 100644
index 0000000..cd8e39c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java
@@ -0,0 +1,279 @@
+/*
+ * 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.accessibility.magnification;
+
+import static com.android.server.accessibility.AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+import android.view.Display;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
+import android.view.accessibility.MagnificationAnimationCallback;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.accessibility.AccessibilityManagerService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for MagnificationController.
+ */
+@RunWith(AndroidJUnit4.class)
+public class MagnificationTransitionControllerTest {
+
+ private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
+ private static final Region MAGNIFICATION_REGION = new Region(0, 0, 500, 600);
+ private static final float MAGNIFIED_CENTER_X = 100;
+ private static final float MAGNIFIED_CENTER_Y = 200;
+ private static final float DEFAULT_SCALE = 3f;
+ private static final int CURRENT_USER_ID = UserHandle.USER_CURRENT;
+ private static final int MODE_WINDOW = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+ private static final int MODE_FULLSCREEN =
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+
+ @Mock private AccessibilityManagerService mService;
+ @Mock private MagnificationTransitionController.TransitionCallBack mTransitionCallBack;
+ @Mock private Context mContext;
+ @Mock private FullScreenMagnificationController mScreenMagnificationController;
+ @Captor private ArgumentCaptor<MagnificationAnimationCallback> mCallbackArgumentCaptor;
+
+ private MockWindowMagnificationConnection mMockConnection;
+ private WindowMagnificationManager mWindowMagnificationManager;
+ private MockContentResolver mMockResolver;
+ private MagnificationTransitionController mMagnificationTransitionController;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ FakeSettingsProvider.clearSettingsProvider();
+ mMockResolver = new MockContentResolver();
+ mMockResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ when(mContext.getContentResolver()).thenReturn(mMockResolver);
+ Settings.Secure.putFloatForUser(mMockResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, DEFAULT_SCALE,
+ CURRENT_USER_ID);
+ mWindowMagnificationManager = new WindowMagnificationManager(mContext, CURRENT_USER_ID);
+ when(mService.getFullScreenMagnificationController()).thenReturn(
+ mScreenMagnificationController);
+ when(mService.getWindowMagnificationMgr()).thenReturn(mWindowMagnificationManager);
+ mMockConnection = new MockWindowMagnificationConnection(true);
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mMagnificationTransitionController = new MagnificationTransitionController(mService,
+ new Object());
+ }
+
+ @After
+ public void tearDown() {
+ FakeSettingsProvider.clearSettingsProvider();
+ }
+
+ @Test
+ public void transitionToWindowMode_notMagnifying_doNothing() throws RemoteException {
+ setMagnificationModeSettings(MODE_FULLSCREEN);
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_WINDOW,
+ mTransitionCallBack);
+
+ verify(mTransitionCallBack).onResult(true);
+ verify(mScreenMagnificationController, never()).reset(anyInt(),
+ any(MagnificationAnimationCallback.class));
+ verify(mMockConnection.getConnection(), never()).enableWindowMagnification(anyInt(),
+ anyFloat(), anyFloat(), anyFloat(),
+ nullable(IRemoteMagnificationAnimationCallback.class));
+ }
+
+ @Test
+ public void transitionToWindowMode_fullScreenMagnifying_disableFullScreenAndEnableWindow()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_WINDOW,
+ mTransitionCallBack);
+
+ verify(mScreenMagnificationController).reset(eq(TEST_DISPLAY),
+ mCallbackArgumentCaptor.capture());
+ mCallbackArgumentCaptor.getValue().onResult(true);
+ mMockConnection.invokeCallbacks();
+ verify(mTransitionCallBack).onResult(true);
+ assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
+ }
+
+ @Test
+ public void transitionToWindowMode_disablingWindowMode_enablingWindowWithFormerCenter()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_WINDOW);
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_FULLSCREEN,
+ mTransitionCallBack);
+
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_WINDOW,
+ mTransitionCallBack);
+
+ mMockConnection.invokeCallbacks();
+ verify(mTransitionCallBack).onResult(true);
+ assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0);
+ assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0);
+ }
+
+ @Test
+ public void transitionToFullScreenMode_windowMagnifying_disableWindowAndEnableFullScreen()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_WINDOW);
+
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_FULLSCREEN,
+ mTransitionCallBack);
+ mMockConnection.invokeCallbacks();
+
+ assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY,
+ DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
+ true, MAGNIFICATION_GESTURE_HANDLER_ID);
+ verify(mTransitionCallBack).onResult(true);
+ }
+
+ @Test
+ public void transitionToFullScreen_centerNotInTheBounds_magnifyTheCenterOfMagnificationBounds()
+ throws RemoteException {
+ final Rect magnificationBounds = MAGNIFICATION_REGION.getBounds();
+ final PointF magnifiedCenter = new PointF(magnificationBounds.right + 100,
+ magnificationBounds.bottom + 100);
+ setMagnificationEnabled(MODE_WINDOW, magnifiedCenter.x, magnifiedCenter.y);
+
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_FULLSCREEN,
+ mTransitionCallBack);
+ mMockConnection.invokeCallbacks();
+
+ assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE,
+ magnificationBounds.exactCenterX(), magnificationBounds.exactCenterY(), true,
+ MAGNIFICATION_GESTURE_HANDLER_ID);
+ verify(mTransitionCallBack).onResult(true);
+ }
+
+ @Test
+ public void transitionToFullScreenMode_disablingFullScreen_enableFullScreenWithFormerCenter()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_FULLSCREEN);
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_WINDOW,
+ mTransitionCallBack);
+
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_FULLSCREEN,
+ mTransitionCallBack);
+
+ verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY,
+ DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
+ true, MAGNIFICATION_GESTURE_HANDLER_ID);
+ verify(mTransitionCallBack).onResult(true);
+ }
+
+
+ @Test
+ public void interruptDuringTransitionToFullScreenMode_windowMagnifying_notifyTransitionFailed()
+ throws RemoteException {
+ setMagnificationEnabled(MODE_WINDOW);
+ mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ MODE_FULLSCREEN,
+ mTransitionCallBack);
+
+ // Enable window magnification while animating.
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE,
+ Float.NaN, Float.NaN, null);
+ mMockConnection.invokeCallbacks();
+
+ assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ verify(mScreenMagnificationController, never()).setScaleAndCenter(TEST_DISPLAY,
+ DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
+ true, MAGNIFICATION_GESTURE_HANDLER_ID);
+ verify(mTransitionCallBack).onResult(false);
+ }
+
+ private void setMagnificationEnabled(int mode) throws RemoteException {
+ setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
+ }
+
+ private void setMagnificationEnabled(int mode, float centerX, float centerY)
+ throws RemoteException {
+ setMagnificationModeSettings(mode);
+ Mockito.reset(mScreenMagnificationController);
+ doAnswer(invocation -> {
+ final Region outRegion = invocation.getArgument(1);
+ outRegion.set(MAGNIFICATION_REGION);
+ return null;
+ }).when(mScreenMagnificationController).getMagnificationRegion(anyInt(), any(Region.class));
+
+ final boolean windowMagnifying = mWindowMagnificationManager.isWindowMagnifierEnabled(
+ TEST_DISPLAY);
+ if (windowMagnifying) {
+ mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+ mMockConnection.invokeCallbacks();
+ }
+ if (mode == MODE_FULLSCREEN) {
+ when(mScreenMagnificationController.isMagnifying(TEST_DISPLAY)).thenReturn(true);
+ when(mScreenMagnificationController.getPersistedScale()).thenReturn(DEFAULT_SCALE);
+ when(mScreenMagnificationController.getScale(TEST_DISPLAY)).thenReturn(DEFAULT_SCALE);
+ when(mScreenMagnificationController.getCenterX(TEST_DISPLAY)).thenReturn(
+ centerX);
+ when(mScreenMagnificationController.getCenterY(TEST_DISPLAY)).thenReturn(
+ centerY);
+ } else {
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE,
+ centerX, centerY, null);
+ mMockConnection.invokeCallbacks();
+ }
+ }
+
+ private void setMagnificationModeSettings(int mode) {
+ Settings.Secure.putIntForUser(mMockResolver,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, mode, CURRENT_USER_ID);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java
index f896d75..2a53504 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java
@@ -25,12 +25,13 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.annotation.Nullable;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
-import android.os.RemoteCallback;
import android.os.RemoteException;
import android.view.Display;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
@@ -38,16 +39,28 @@
* Mocks the basic logic of window magnification in System UI. We assume the screen size is
* unlimited, so source bounds is always on the center of the mirror window bounds.
*/
-class MockWindowMagnificationConnection {
+class MockWindowMagnificationConnection {
public static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
private final IWindowMagnificationConnection mConnection;
private final Binder mBinder;
+ private final boolean mSuspendCallback;
+ private boolean mHasPendingCallback = false;
+ private boolean mWindowMagnificationEnabled = false;
private IBinder.DeathRecipient mDeathRecipient;
private IWindowMagnificationConnectionCallback mIMirrorWindowCallback;
+
private Rect mMirrorWindowFrame = new Rect(0, 0, 500, 500);
+ private float mScale = 2.0f;
+ private Rect mSourceBounds = new Rect();
+ private IRemoteMagnificationAnimationCallback mAnimationCallback;
MockWindowMagnificationConnection() throws RemoteException {
+ this(false);
+ }
+
+ MockWindowMagnificationConnection(boolean suspendCallback) throws RemoteException {
+ mSuspendCallback = suspendCallback;
mConnection = mock(IWindowMagnificationConnection.class);
mBinder = mock(Binder.class);
when(mConnection.asBinder()).thenReturn(mBinder);
@@ -66,33 +79,55 @@
}
private void stubConnection() throws RemoteException {
- doAnswer((invocation) -> {
- final int displayId = invocation.getArgument(0);
- if (displayId != TEST_DISPLAY) {
- throw new IllegalArgumentException("only support default display :" + displayId);
- }
- computeMirrorWindowFrame(invocation.getArgument(2), invocation.getArgument(3));
- final RemoteCallback callback = invocation.getArgument(4);
- if (callback != null) {
- callback.sendResult(null);
- }
- mIMirrorWindowCallback.onWindowMagnifierBoundsChanged(TEST_DISPLAY,
- mMirrorWindowFrame);
- return null;
- }).when(mConnection).enableWindowMagnification(anyInt(),
- anyFloat(), anyFloat(), anyFloat(), nullable(RemoteCallback.class));
+ stubEnableWindowMagnification();
+ stubDisableWindowMagnification();
+ }
+ private void stubEnableWindowMagnification() throws RemoteException {
doAnswer((invocation) -> {
final int displayId = invocation.getArgument(0);
if (displayId != TEST_DISPLAY) {
throw new IllegalArgumentException("only support default display :" + displayId);
}
- final RemoteCallback callback = invocation.getArgument(1);
- if (callback != null) {
- callback.sendResult(null);
+ mWindowMagnificationEnabled = true;
+ final float scale = invocation.getArgument(1);
+ mScale = Float.isNaN(scale) ? mScale : scale;
+ computeMirrorWindowFrame(invocation.getArgument(2), invocation.getArgument(3));
+ setAnimationCallback(invocation.getArgument(4));
+ computeSourceBounds();
+ mHasPendingCallback = true;
+ if (!mSuspendCallback) {
+ invokeCallbacksInternal(true);
}
return null;
- }).when(mConnection).disableWindowMagnification(anyInt(), nullable(RemoteCallback.class));
+ }).when(mConnection).enableWindowMagnification(anyInt(), anyFloat(), anyFloat(), anyFloat(),
+ nullable(IRemoteMagnificationAnimationCallback.class));
+ }
+
+ private void stubDisableWindowMagnification() throws RemoteException {
+ doAnswer((invocation) -> {
+ final int displayId = invocation.getArgument(0);
+ if (displayId != TEST_DISPLAY) {
+ throw new IllegalArgumentException("only support default display :" + displayId);
+ }
+ setAnimationCallback(invocation.getArgument(1));
+ mHasPendingCallback = true;
+ if (!mSuspendCallback) {
+ invokeCallbacksInternal(true);
+ }
+ return null;
+ }).when(mConnection).disableWindowMagnification(anyInt(),
+ nullable(IRemoteMagnificationAnimationCallback.class));
+ }
+
+ private void computeSourceBounds() {
+ final int halfWidth = mMirrorWindowFrame.width() / 2;
+ final int halfHeight = mMirrorWindowFrame.height() / 2;
+ final int left = mMirrorWindowFrame.left + (halfWidth - (int) (halfWidth / mScale));
+ final int right = mMirrorWindowFrame.right - (halfWidth - (int) (halfWidth / mScale));
+ final int top = mMirrorWindowFrame.top + (halfHeight - (int) (halfHeight / mScale));
+ final int bottom = mMirrorWindowFrame.bottom - (halfHeight - (int) (halfHeight / mScale));
+ mSourceBounds.set(left, top, right, bottom);
}
private void computeMirrorWindowFrame(float centerX, float centerY) {
@@ -103,6 +138,13 @@
mMirrorWindowFrame.offset((int) offsetX, (int) offsetY);
}
+ private void sendAnimationEndCallbackIfNeeded(boolean success) throws RemoteException {
+ if (mAnimationCallback != null) {
+ mAnimationCallback.onResult(success);
+ mAnimationCallback = null;
+ }
+ }
+
IWindowMagnificationConnection getConnection() {
return mConnection;
}
@@ -119,8 +161,38 @@
return mIMirrorWindowCallback;
}
- public Rect getMirrorWindowFrame() {
+ Rect getMirrorWindowFrame() {
return new Rect(mMirrorWindowFrame);
}
+
+ void invokeCallbacks() throws RemoteException {
+ if (!mSuspendCallback) {
+ throw new IllegalStateException("Invoke callbacks automatically");
+ }
+ invokeCallbacksInternal(true);
+ }
+
+ private void invokeCallbacksInternal(boolean success) throws RemoteException {
+ if (!mHasPendingCallback) {
+ throw new IllegalStateException("There is no any pending callbacks");
+ }
+ if (mWindowMagnificationEnabled && mIMirrorWindowCallback != null) {
+ mIMirrorWindowCallback.onWindowMagnifierBoundsChanged(TEST_DISPLAY,
+ mMirrorWindowFrame);
+ mIMirrorWindowCallback.onSourceBoundsChanged(TEST_DISPLAY,
+ mSourceBounds);
+ }
+ sendAnimationEndCallbackIfNeeded(success);
+ mHasPendingCallback = false;
+ }
+
+ private void setAnimationCallback(
+ @Nullable IRemoteMagnificationAnimationCallback animationCallback)
+ throws RemoteException {
+ if (mAnimationCallback != null) {
+ invokeCallbacksInternal(false);
+ }
+ mAnimationCallback = animationCallback;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
index 9ef65d9..c88bc3b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
@@ -17,14 +17,17 @@
package com.android.server.accessibility.magnification;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
-import android.os.RemoteCallback;
import android.os.RemoteException;
import android.provider.Settings;
import android.view.Display;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
+import android.view.accessibility.MagnificationAnimationCallback;
import org.junit.Before;
import org.junit.Test;
@@ -40,26 +43,29 @@
private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
- @Mock
private IWindowMagnificationConnection mConnection;
@Mock
private IWindowMagnificationConnectionCallback mCallback;
@Mock
- private RemoteCallback.OnResultListener mOnResultListener;
- private RemoteCallback mRemoteCallback;
+ private MagnificationAnimationCallback mAnimationCallback;
+
+ private MockWindowMagnificationConnection mMockWindowMagnificationConnection;
private WindowMagnificationConnectionWrapper mConnectionWrapper;
@Before
- public void setUp() {
+ public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
+ mMockWindowMagnificationConnection = new MockWindowMagnificationConnection();
+ mConnection = mMockWindowMagnificationConnection.getConnection();
mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection);
- mRemoteCallback = new RemoteCallback(mOnResultListener);
}
@Test
public void enableWindowMagnification() throws RemoteException {
- mConnectionWrapper.enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f, mRemoteCallback);
- verify(mConnection).enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f, mRemoteCallback);
+ mConnectionWrapper.enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f,
+ mAnimationCallback);
+
+ verify(mAnimationCallback).onResult(true);
}
@Test
@@ -70,8 +76,11 @@
@Test
public void disableWindowMagnification() throws RemoteException {
- mConnectionWrapper.disableWindowMagnification(TEST_DISPLAY, mRemoteCallback);
- verify(mConnection).disableWindowMagnification(TEST_DISPLAY, mRemoteCallback);
+ mConnectionWrapper.disableWindowMagnification(TEST_DISPLAY, mAnimationCallback);
+
+ verify(mConnection).disableWindowMagnification(eq(TEST_DISPLAY),
+ any(IRemoteMagnificationAnimationCallback.class));
+ verify(mAnimationCallback).onResult(true);
}
@Test
@@ -99,5 +108,4 @@
mConnectionWrapper.setConnectionCallback(mCallback);
verify(mConnection).setConnectionCallback(mCallback);
}
-
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index dcb1262..89b0a03 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -47,7 +47,9 @@
import android.view.Display;
import android.view.InputDevice;
import android.view.MotionEvent;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
+import android.view.accessibility.MagnificationAnimationCallback;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
@@ -73,7 +75,7 @@
@Mock
private StatusBarManagerInternal mMockStatusBarManagerInternal;
@Mock
- private Runnable mEndCallback;
+ private MagnificationAnimationCallback mAnimationCallback;
private MockContentResolver mResolver;
private WindowMagnificationManager mWindowMagnificationManager;
@@ -177,9 +179,11 @@
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f,
- mEndCallback);
+ mAnimationCallback);
- verify(mEndCallback).run();
+ verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
+ eq(200f), eq(300f), any(IRemoteMagnificationAnimationCallback.class));
+ verify(mAnimationCallback).onResult(true);
}
@Test
@@ -194,13 +198,17 @@
}
@Test
- public void disableWithCallback_hasConnectionAndEnabled_disableWindowMagnification() {
+ public void disableWithCallback_hasConnectionAndEnabled_disableWindowMagnification()
+ throws RemoteException {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
- mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false, mEndCallback);
+ mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false,
+ mAnimationCallback);
- verify(mEndCallback).run();
+ verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY),
+ any(IRemoteMagnificationAnimationCallback.class));
+ verify(mAnimationCallback).onResult(true);
}
@Test
@@ -352,6 +360,15 @@
assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
}
+ @Test
+ public void centerGetter_enabledOnTestDisplay_expectedValues() {
+ mWindowMagnificationManager.requestConnection(true);
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
+
+ assertEquals(mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 100f);
+ assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
+ }
+
private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) {
final int len = pointersLocation.length;
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
index 8871348..9a5c633 100644
--- a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
@@ -176,7 +176,7 @@
}
/**
- * Sends dummy widget updates to {@link #mManager}.
+ * Sends placeholder widget updates to {@link #mManager}.
* @param widgetId widget to update
* @param viewIds a list of view ids for which
* {@link AppWidgetManager#notifyAppWidgetViewDataChanged} will be called
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/DummyAppWidget.java b/services/tests/servicestests/src/com/android/server/appwidget/DummyAppWidget.java
index 803119f4..fd99b21 100644
--- a/services/tests/servicestests/src/com/android/server/appwidget/DummyAppWidget.java
+++ b/services/tests/servicestests/src/com/android/server/appwidget/DummyAppWidget.java
@@ -21,7 +21,7 @@
import android.content.Intent;
/**
- * Dummy widget for testing
+ * Placeholder widget for testing
*/
public class DummyAppWidget extends BroadcastReceiver {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index 94cad1e..cc1fdab 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -83,7 +83,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
- // Dummy test config
+ // Placeholder test config
final String[] config = {
"0:2:15", // ID0:Fingerprint:Strong
"1:4:15", // ID1:Iris:Strong
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 8759077..4ce6411 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -4392,6 +4392,16 @@
public void testSecondaryLockscreen_nonSupervisionApp() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ // Ensure packages are *not* flagged as test_only.
+ doReturn(new ApplicationInfo()).when(getServices().ipackageManager).getApplicationInfo(
+ eq(admin1.getPackageName()),
+ anyInt(),
+ eq(CALLER_USER_HANDLE));
+ doReturn(new ApplicationInfo()).when(getServices().ipackageManager).getApplicationInfo(
+ eq(admin2.getPackageName()),
+ anyInt(),
+ eq(CALLER_USER_HANDLE));
+
// Initial state is disabled.
assertFalse(dpm.isSecondaryLockscreenEnabled(UserHandle.of(CALLER_USER_HANDLE)));
@@ -5138,7 +5148,7 @@
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
assertTrue(dpms.isNotificationListenerServicePermitted(packageName, userId));
- // Attempt to set to empty list (which means no listener is whitelisted)
+ // Attempt to set to empty list (which means no listener is allowlisted)
mContext.binder.callingUid = adminUid;
assertFalse(dpms.setPermittedCrossProfileNotificationListeners(
admin1, Collections.emptyList()));
@@ -5212,7 +5222,7 @@
assertTrue(dpms.isNotificationListenerServicePermitted(
systemListener, MANAGED_PROFILE_USER_ID));
- // Setting only one package in the whitelist
+ // Setting only one package in the allowlist
mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
assertTrue(dpms.setPermittedCrossProfileNotificationListeners(
admin1, Collections.singletonList(permittedListener)));
@@ -5226,11 +5236,11 @@
permittedListener, MANAGED_PROFILE_USER_ID));
assertFalse(dpms.isNotificationListenerServicePermitted(
notPermittedListener, MANAGED_PROFILE_USER_ID));
- // System packages are always allowed (even if not in the whitelist)
+ // System packages are always allowed (even if not in the allowlist)
assertTrue(dpms.isNotificationListenerServicePermitted(
systemListener, MANAGED_PROFILE_USER_ID));
- // Setting an empty whitelist - only system listeners allowed
+ // Setting an empty allowlist - only system listeners allowed
mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
assertTrue(dpms.setPermittedCrossProfileNotificationListeners(
admin1, Collections.emptyList()));
@@ -5241,11 +5251,11 @@
permittedListener, MANAGED_PROFILE_USER_ID));
assertFalse(dpms.isNotificationListenerServicePermitted(
notPermittedListener, MANAGED_PROFILE_USER_ID));
- // System packages are always allowed (even if not in the whitelist)
+ // System packages are always allowed (even if not in the allowlist)
assertTrue(dpms.isNotificationListenerServicePermitted(
systemListener, MANAGED_PROFILE_USER_ID));
- // Setting a null whitelist - all listeners allowed
+ // Setting a null allowlist - all listeners allowed
mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
assertTrue(dpms.setPermittedCrossProfileNotificationListeners(admin1, null));
assertNull(dpms.getPermittedCrossProfileNotificationListeners(admin1));
@@ -5293,7 +5303,7 @@
assertTrue(dpms.isNotificationListenerServicePermitted(
systemListener, UserHandle.USER_SYSTEM));
- // Setting an empty whitelist - only system listeners allowed in managed profile, but
+ // Setting an empty allowlist - only system listeners allowed in managed profile, but
// all allowed in primary profile
mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
assertTrue(dpms.setPermittedCrossProfileNotificationListeners(
diff --git a/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java
index e8e6ded..df672c9 100644
--- a/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java
@@ -294,7 +294,7 @@
AmbientBrightnessStatsTracker statsTracker = getTestStatsTracker();
ArrayList<AmbientBrightnessDayStats> userStats;
float[] expectedStats;
- // Generate some dummy data
+ // Generate some placeholder data
// Data: very old which should not be read
statsTracker.start();
statsTracker.add(0, 0.05f);
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index 48dda01..ae966aa 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -466,7 +466,7 @@
Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS,
DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT);
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources);
- // Let's start with a sanity check:
+ // Let's start with a validity check:
assertEquals(y1, strategy.getBrightness(x1), 0.01f /* tolerance */);
assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */);
assertEquals(y3, strategy.getBrightness(x3), 0.01f /* tolerance */);
@@ -494,7 +494,7 @@
Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS,
DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT);
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources);
- // Sanity check:
+ // Validity check:
assertEquals(y1, strategy.getBrightness(x1), 0.01f /* tolerance */);
assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */);
assertEquals(y3, strategy.getBrightness(x3), 0.01f /* tolerance */);
@@ -540,7 +540,7 @@
Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS,
DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT);
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources);
- // Sanity, as per tradition:
+ // Validity, as per tradition:
assertEquals(y0, strategy.getBrightness(x0), 0.01f /* tolerance */);
assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */);
assertEquals(y4, strategy.getBrightness(x4), 0.01f /* tolerance */);
diff --git a/services/tests/servicestests/src/com/android/server/emergency/EmergencyAffordanceServiceTest.java b/services/tests/servicestests/src/com/android/server/emergency/EmergencyAffordanceServiceTest.java
index d438a0e..3ecff91 100644
--- a/services/tests/servicestests/src/com/android/server/emergency/EmergencyAffordanceServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/emergency/EmergencyAffordanceServiceTest.java
@@ -448,7 +448,7 @@
List<SubscriptionInfo> subInfos = new ArrayList<>(2);
// Test with Multiple SIMs. SIM1 is a non-EA SIM
- // Only country iso is valuable, all other info are filled with dummy values
+ // Only country iso is valuable, all other info are filled with placeholder values
SubscriptionInfo subInfo = new SubscriptionInfo(1, "890126042XXXXXXXXXXX", 0, "T-mobile",
"T-mobile", 0, 255, "12345", 0, null,
"310", "226", NON_EMERGENCY_ISO_CODE, false, null, null);
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 83df406..c0d5f7b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -19,11 +19,18 @@
import static com.google.common.truth.Truth.assertThat;
-import android.annotation.Nullable;
-import android.app.Instrumentation;
-import android.hardware.hdmi.HdmiDeviceInfo;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.media.AudioManager;
+import android.os.Handler;
+import android.os.IPowerManager;
+import android.os.IThermalService;
import android.os.Looper;
+import android.os.PowerManager;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -31,10 +38,11 @@
import androidx.test.filters.SmallTest;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@@ -44,7 +52,7 @@
@RunWith(JUnit4.class)
public class ArcInitiationActionFromAvrTest {
- private HdmiDeviceInfo mDeviceInfoForTests;
+ private Context mContextSpy;
private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
private HdmiCecController mHdmiCecController;
private HdmiControlService mHdmiControlService;
@@ -52,59 +60,46 @@
private ArcInitiationActionFromAvr mAction;
private TestLooper mTestLooper = new TestLooper();
- private boolean mSendCecCommandSuccess;
- private boolean mShouldDispatchARCInitiated;
- private boolean mArcInitSent;
- private boolean mRequestActiveSourceSent;
- private Instrumentation mInstrumentation;
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
- @Before
- public void setUp() {
- mDeviceInfoForTests = new HdmiDeviceInfo(1000, 1);
+ @Mock private IPowerManager mIPowerManagerMock;
+ @Mock private IThermalService mIThermalServiceMock;
+ @Mock private AudioManager mAudioManager;
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+
+ PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService =
- new HdmiControlService(mInstrumentation.getTargetContext()) {
- @Override
- void sendCecCommand(
- HdmiCecMessage command, @Nullable SendMessageCallback callback) {
- switch (command.getOpcode()) {
- case Constants.MESSAGE_REQUEST_ACTIVE_SOURCE:
- if (callback != null) {
- callback.onSendCompleted(
- mSendCecCommandSuccess
- ? SendMessageResult.SUCCESS
- : SendMessageResult.NACK);
- }
- mRequestActiveSourceSent = true;
- break;
- case Constants.MESSAGE_INITIATE_ARC:
- if (callback != null) {
- callback.onSendCompleted(
- mSendCecCommandSuccess
- ? SendMessageResult.SUCCESS
- : SendMessageResult.NACK);
- }
- mArcInitSent = true;
- if (mShouldDispatchARCInitiated) {
- mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
- HdmiCecMessageBuilder.buildReportArcInitiated(
- Constants.ADDR_TV,
- Constants.ADDR_AUDIO_SYSTEM));
- }
- break;
- default:
- }
- }
-
+ new HdmiControlService(mContextSpy) {
@Override
boolean isPowerStandby() {
return false;
}
@Override
+ void wakeUp() {
+ }
+
+ @Override
+ PowerManager getPowerManager() {
+ return powerManager;
+ }
+
+ @Override
+ AudioManager getAudioManager() {
+ return mAudioManager;
+ }
+
+ @Override
boolean isAddressAllocated() {
return true;
}
@@ -115,23 +110,11 @@
}
};
- mHdmiCecLocalDeviceAudioSystem =
- new HdmiCecLocalDeviceAudioSystem(mHdmiControlService) {
- @Override
- HdmiDeviceInfo getDeviceInfo() {
- return mDeviceInfoForTests;
- }
-
- @Override
- void setArcStatus(boolean enabled) {
- // do nothing
- }
-
- @Override
- protected boolean isSystemAudioActivated() {
- return true;
- }
- };
+ mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService) {
+ @Override
+ protected void setPreferredAddress(int addr) {
+ }
+ };
mHdmiCecLocalDeviceAudioSystem.init();
Looper looper = mTestLooper.getLooper();
@@ -150,18 +133,88 @@
mTestLooper.dispatchAll();
}
- @Ignore("b/120845532")
@Test
- public void arcInitiation_requestActiveSource() {
- mSendCecCommandSuccess = true;
- mShouldDispatchARCInitiated = true;
- mRequestActiveSourceSent = false;
- mArcInitSent = false;
+ public void arcInitiation_initiated() {
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+ mTestLooper.dispatchAll();
+ HdmiCecMessage initiateArc = HdmiCecMessageBuilder.buildInitiateArc(
+ Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_TV);
+ assertThat(mNativeWrapper.getResultMessages()).contains(initiateArc);
+
+ mHdmiControlService.sendCecCommand(
+ HdmiCecMessageBuilder.buildReportArcInitiated(
+ Constants.ADDR_TV,
+ Constants.ADDR_AUDIO_SYSTEM));
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isTrue();
+ }
+
+ @Test
+ public void arcInitiation_sendFailed() {
+ mNativeWrapper.setMessageSendResult(Constants.MESSAGE_INITIATE_ARC, SendMessageResult.NACK);
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+ mTestLooper.dispatchAll();
+ HdmiCecMessage initiateArc = HdmiCecMessageBuilder.buildInitiateArc(
+ Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(initiateArc);
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
+ }
+
+ @Test
+ public void arcInitiation_terminated() {
mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
mTestLooper.dispatchAll();
- assertThat(mArcInitSent).isTrue();
- assertThat(mRequestActiveSourceSent).isTrue();
+ HdmiCecMessage initiateArc = HdmiCecMessageBuilder.buildInitiateArc(
+ Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(initiateArc);
+
+ mHdmiControlService.handleCecCommand(HdmiCecMessageBuilder.buildReportArcTerminated(
+ Constants.ADDR_TV,
+ Constants.ADDR_AUDIO_SYSTEM));
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
+ }
+
+ @Test
+ public void arcInitiation_abort() {
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage initiateArc = HdmiCecMessageBuilder.buildInitiateArc(
+ Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(initiateArc);
+
+ mHdmiControlService.handleCecCommand(
+ HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ Constants.ADDR_TV,
+ Constants.ADDR_AUDIO_SYSTEM, Constants.MESSAGE_INITIATE_ARC,
+ Constants.ABORT_REFUSED));
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
+ }
+
+ //Fail
+ @Test
+ public void arcInitiation_timeout() {
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage initiateArc = HdmiCecMessageBuilder.buildInitiateArc(
+ Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(initiateArc);
+
+ mTestLooper.moveTimeForward(1001);
+ mTestLooper.dispatchAll();
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isTrue();
}
}
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 dc326ee..f986a70 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -15,13 +15,22 @@
*/
package com.android.server.hdmi;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+
import static com.google.common.truth.Truth.assertThat;
-import android.annotation.Nullable;
-import android.app.Instrumentation;
-import android.hardware.hdmi.HdmiDeviceInfo;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.media.AudioManager;
+import android.os.Handler;
+import android.os.IPowerManager;
+import android.os.IThermalService;
import android.os.Looper;
+import android.os.PowerManager;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -29,10 +38,13 @@
import androidx.test.filters.SmallTest;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
/** Tests for {@link ArcTerminationActionFromAvr} */
@SmallTest
@@ -40,45 +52,47 @@
@RunWith(JUnit4.class)
public class ArcTerminationActionFromAvrTest {
- private HdmiDeviceInfo mDeviceInfoForTests;
+ private Context mContextSpy;
private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
private ArcTerminationActionFromAvr mAction;
+ private HdmiCecController mHdmiCecController;
+ private HdmiControlService mHdmiControlService;
+ private FakeNativeWrapper mNativeWrapper;
+
private TestLooper mTestLooper = new TestLooper();
- private boolean mSendCecCommandSuccess;
- private boolean mShouldDispatchReportArcTerminated;
- private Instrumentation mInstrumentation;
- @Nullable private Boolean mArcEnabled = null;
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+
+ @Mock private IPowerManager mIPowerManagerMock;
+ @Mock private IThermalService mIThermalServiceMock;
+ @Mock private AudioManager mAudioManager;
@Before
- public void setUp() {
- mDeviceInfoForTests = new HdmiDeviceInfo(1000, 1);
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- HdmiControlService hdmiControlService =
- new HdmiControlService(mInstrumentation.getTargetContext()) {
+ PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mIPowerManagerMock.isInteractive()).thenReturn(true);
+
+ mHdmiControlService =
+ new HdmiControlService(mContextSpy) {
@Override
- void sendCecCommand(
- HdmiCecMessage command, @Nullable SendMessageCallback callback) {
- switch (command.getOpcode()) {
- case Constants.MESSAGE_TERMINATE_ARC:
- if (callback != null) {
- callback.onSendCompleted(
- mSendCecCommandSuccess
- ? SendMessageResult.SUCCESS
- : SendMessageResult.NACK);
- }
- if (mShouldDispatchReportArcTerminated) {
- mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
- HdmiCecMessageBuilder.buildReportArcTerminated(
- Constants.ADDR_TV,
- mHdmiCecLocalDeviceAudioSystem.mAddress));
- }
- break;
- default:
- throw new IllegalArgumentException("Unexpected message");
- }
+ void wakeUp() {
+ }
+
+ @Override
+ PowerManager getPowerManager() {
+ return powerManager;
+ }
+
+ @Override
+ AudioManager getAudioManager() {
+ return mAudioManager;
}
@Override
@@ -97,55 +111,71 @@
}
};
- mHdmiCecLocalDeviceAudioSystem =
- new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
- @Override
- HdmiDeviceInfo getDeviceInfo() {
- return mDeviceInfoForTests;
- }
-
- @Override
- void setArcStatus(boolean enabled) {
- mArcEnabled = enabled;
- }
- };
- mHdmiCecLocalDeviceAudioSystem.init();
Looper looper = mTestLooper.getLooper();
- hdmiControlService.setIoLooper(looper);
+ mHdmiControlService.setIoLooper(looper);
+ mNativeWrapper = new FakeNativeWrapper();
+ mHdmiCecController =
+ HdmiCecController.createWithNativeWrapper(this.mHdmiControlService, mNativeWrapper);
+ mHdmiControlService.setCecController(mHdmiCecController);
+ mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+ mHdmiControlService.initPortInfo();
+ mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService) {
+ @Override
+ protected void setPreferredAddress(int addr) {
+ }
+ };
+ mHdmiCecLocalDeviceAudioSystem.init();
mAction = new ArcTerminationActionFromAvr(mHdmiCecLocalDeviceAudioSystem);
+
+ mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mHdmiCecLocalDeviceAudioSystem.setArcStatus(true);
+ mTestLooper.dispatchAll();
}
@Test
- @Ignore("b/120845532")
- public void testSendMessage_notSuccess() {
- mSendCecCommandSuccess = false;
- mShouldDispatchReportArcTerminated = false;
+ public void testSendMessage_sendFailed() {
+ mNativeWrapper.setMessageSendResult(Constants.MESSAGE_TERMINATE_ARC,
+ SendMessageResult.NACK);
mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
-
mTestLooper.dispatchAll();
- assertThat(mArcEnabled).isNull();
+ HdmiCecMessage terminateArc = HdmiCecMessageBuilder.buildTerminateArc(
+ Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(terminateArc);
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
}
@Test
- public void testReportArcTerminated_notReceived() {
- mSendCecCommandSuccess = true;
- mShouldDispatchReportArcTerminated = false;
+ public void testReportArcTerminated_timeout() {
mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
-
- mTestLooper.moveTimeForward(1000);
mTestLooper.dispatchAll();
- assertThat(mArcEnabled).isNull();
+ HdmiCecMessage terminateArc = HdmiCecMessageBuilder.buildTerminateArc(
+ Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(terminateArc);
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isTrue();
}
@Test
public void testReportArcTerminated_received() {
- mSendCecCommandSuccess = true;
- mShouldDispatchReportArcTerminated = true;
mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
-
- mTestLooper.moveTimeForward(1000);
mTestLooper.dispatchAll();
- assertThat(mArcEnabled).isFalse();
+ HdmiCecMessage terminateArc = HdmiCecMessageBuilder.buildTerminateArc(
+ Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(terminateArc);
+
+ HdmiCecMessage arcTerminatedResponse = HdmiCecMessageBuilder.buildReportArcTerminated(
+ Constants.ADDR_TV, Constants.ADDR_AUDIO_SYSTEM);
+
+ mHdmiControlService.handleCecCommand(arcTerminatedResponse);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
}
}
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 7538468..01f0a3d 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -24,6 +24,7 @@
import com.google.common.collect.Iterables;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
/** Fake {@link NativeWrapper} useful for testing. */
@@ -48,6 +49,7 @@
};
private final List<HdmiCecMessage> mResultMessages = new ArrayList<>();
+ private final HashMap<Integer, Integer> mMessageSendResult = new HashMap<>();
private int mMyPhysicalAddress = 0;
private HdmiPortInfo[] mHdmiPortInfo = null;
@@ -65,9 +67,10 @@
if (body.length == 0) {
return mPollAddressResponse[dstAddress];
} else {
- mResultMessages.add(HdmiCecMessageBuilder.of(srcAddress, dstAddress, body));
+ HdmiCecMessage message = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
+ mResultMessages.add(message);
+ return mMessageSendResult.getOrDefault(message.getOpcode(), SendMessageResult.SUCCESS);
}
- return SendMessageResult.SUCCESS;
}
@Override
@@ -132,6 +135,10 @@
mPollAddressResponse[logicalAddress] = response;
}
+ public void setMessageSendResult(int opcode, int result) {
+ mMessageSendResult.put(opcode, result);
+ }
+
@VisibleForTesting
protected void setPhysicalAddress(int physicalAddress) {
mMyPhysicalAddress = physicalAddress;
diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java
index 0488745..441cd4b 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java
@@ -178,7 +178,7 @@
.isEqualTo(IntegrityCheckResult.Effect.DENY);
}
- /** Returns a builder with all fields filled with some dummy data. */
+ /** Returns a builder with all fields filled with some placeholder data. */
private AppInstallMetadata.Builder getAppInstallMetadataBuilder() {
return new AppInstallMetadata.Builder()
.setPackageName("abc")
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index cae7b57..deaeb46 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -505,7 +505,7 @@
first.getTransientExtras().toString(), second.getTransientExtras().toString());
// Since people can forget to add tests here for new fields, do one last
- // sanity check based on bits-on-wire equality.
+ // validity check based on bits-on-wire equality.
final byte[] firstBytes = marshall(first);
final byte[] secondBytes = marshall(second);
if (!Arrays.equals(firstBytes, secondBytes)) {
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
new file mode 100644
index 0000000..dbaad66
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
@@ -0,0 +1,528 @@
+/*
+ * 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.location.timezone;
+
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS;
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN;
+
+import static com.android.server.location.timezone.ControllerImpl.UNCERTAINTY_DELAY;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED;
+import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_DISABLED;
+import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED;
+import static com.android.server.location.timezone.TestSupport.USER1_ID;
+import static com.android.server.location.timezone.TestSupport.USER2_CONFIG_GEO_DETECTION_ENABLED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import static java.util.Arrays.asList;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.location.timezone.LocationTimeZoneEvent;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.util.IndentingPrintWriter;
+
+import com.android.server.timezonedetector.ConfigurationInternal;
+import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+import com.android.server.timezonedetector.TestState;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/** Tests for {@link ControllerImpl}. */
+@Presubmit
+public class ControllerImplTest {
+
+ private static final long ARBITRARY_TIME = 12345L;
+
+ private static final LocationTimeZoneEvent USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1 =
+ createLocationTimeZoneEvent(USER1_ID, EVENT_TYPE_SUCCESS, asList("Europe/London"));
+ private static final LocationTimeZoneEvent USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2 =
+ createLocationTimeZoneEvent(USER1_ID, EVENT_TYPE_SUCCESS, asList("Europe/Paris"));
+ private static final LocationTimeZoneEvent USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT =
+ createLocationTimeZoneEvent(USER1_ID, EVENT_TYPE_UNCERTAIN, null);
+
+ private TestThreadingDomain mTestThreadingDomain;
+ private TestCallback mTestCallback;
+ private TestLocationTimeZoneProvider mTestLocationTimeZoneProvider;
+
+ @Before
+ public void setUp() {
+ // For simplicity, the TestThreadingDomain uses the test's main thread. To execute posted
+ // runnables, the test must call methods on mTestThreadingDomain otherwise those runnables
+ // will never get a chance to execute.
+ mTestThreadingDomain = new TestThreadingDomain();
+ mTestCallback = new TestCallback(mTestThreadingDomain);
+ mTestLocationTimeZoneProvider =
+ new TestLocationTimeZoneProvider(mTestThreadingDomain, "primary");
+ }
+
+ @Test
+ public void initialState_enabled() {
+ ControllerImpl controllerImpl =
+ new ControllerImpl(mTestThreadingDomain, mTestLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestLocationTimeZoneProvider.assertInitialized();
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertNextQueueItemIsDelayed(UNCERTAINTY_DELAY);
+ mTestCallback.assertNoSuggestionMade();
+ }
+
+ @Test
+ public void initialState_disabled() {
+ ControllerImpl controllerImpl =
+ new ControllerImpl(mTestThreadingDomain, mTestLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_DISABLED);
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestLocationTimeZoneProvider.assertInitialized();
+
+ mTestLocationTimeZoneProvider.assertIsDisabled();
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertNoSuggestionMade();
+ }
+
+ @Test
+ public void enabled_uncertaintySuggestionSentIfNoEventReceived() {
+ ControllerImpl controllerImpl =
+ new ControllerImpl(mTestThreadingDomain, mTestLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestCallback.assertNoSuggestionMade();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+
+ // Simulate time passing with no event being received.
+ mTestThreadingDomain.executeNext();
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestCallback.assertUncertainSuggestionMadeAndCommit();
+ mTestThreadingDomain.assertQueueEmpty();
+ }
+
+ @Test
+ public void enabled_uncertaintySuggestionCancelledIfEventReceived() {
+ ControllerImpl controllerImpl =
+ new ControllerImpl(mTestThreadingDomain, mTestLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ mTestCallback.assertNoSuggestionMade();
+
+ // Simulate a location event being received by the provider. This should cause a suggestion
+ // to be made, and the timeout to be cleared.
+ mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertSuggestionMadeAndCommit(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
+ }
+
+ @Test
+ public void enabled_repeatedCertainty() {
+ ControllerImpl controllerImpl =
+ new ControllerImpl(mTestThreadingDomain, mTestLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ mTestCallback.assertNoSuggestionMade();
+
+ // Simulate a location event being received by the provider. This should cause a suggestion
+ // to be made, and the timeout to be cleared.
+ mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertSuggestionMadeAndCommit(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
+
+ // A second, identical event should not cause another suggestion.
+ mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertNoSuggestionMade();
+
+ // And a third, different event should cause another suggestion.
+ mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertSuggestionMadeAndCommit(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds());
+ }
+
+ @Test
+ public void enabled_briefUncertaintyTriggersNoSuggestion() {
+ ControllerImpl controllerImpl =
+ new ControllerImpl(mTestThreadingDomain, mTestLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ mTestCallback.assertNoSuggestionMade();
+
+ // Simulate a location event being received by the provider. This should cause a suggestion
+ // to be made, and the timeout to be cleared.
+ mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertSuggestionMadeAndCommit(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
+
+ // Uncertainty should cause
+ mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+ USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ mTestCallback.assertNoSuggestionMade();
+
+ // And a third event should cause yet another suggestion.
+ mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertSuggestionMadeAndCommit(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds());
+ }
+
+ @Test
+ public void configChanges_enableAndDisable() {
+ ControllerImpl controllerImpl =
+ new ControllerImpl(mTestThreadingDomain, mTestLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_DISABLED);
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestLocationTimeZoneProvider.assertIsDisabled();
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertNoSuggestionMade();
+
+ // Now signal a config change so that geo detection is enabled.
+ testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_ENABLED);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertNextQueueItemIsDelayed(UNCERTAINTY_DELAY);
+ mTestCallback.assertNoSuggestionMade();
+
+ // Now signal a config change so that geo detection is disabled.
+ testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED);
+
+ mTestLocationTimeZoneProvider.assertIsDisabled();
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertNoSuggestionMade();
+ }
+
+ @Test
+ public void configChanges_disableWithPreviousSuggestion() {
+ ControllerImpl controllerImpl =
+ new ControllerImpl(mTestThreadingDomain, mTestLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ mTestCallback.assertNoSuggestionMade();
+
+ // Simulate a location event being received by the provider. This should cause a suggestion
+ // to be made, and the timeout to be cleared.
+ mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertSuggestionMadeAndCommit(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
+
+ // Simulate the user disabling the provider.
+ testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED);
+
+ // Because there had been a previous suggestion, the controller should withdraw it
+ // immediately to let the downstream components know that the provider can no longer be sure
+ // of the time zone.
+ mTestLocationTimeZoneProvider.assertIsDisabled();
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertSuggestionMadeAndCommit(null);
+ }
+
+ @Test
+ public void configChanges_userSwitch_enabledToEnabled() {
+ ControllerImpl controllerImpl =
+ new ControllerImpl(mTestThreadingDomain, mTestLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ // There should be a runnable scheduled to suggest uncertainty if no event is received.
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ mTestCallback.assertNoSuggestionMade();
+
+ // Have the provider suggest a time zone.
+ mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+
+ // Receiving a "success" provider event should cause a suggestion to be made synchronously,
+ // and also clear the scheduled uncertainty suggestion.
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertSuggestionMadeAndCommit(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
+
+ // Simulate the user change (but geo detection still enabled).
+ testEnvironment.simulateConfigChange(USER2_CONFIG_GEO_DETECTION_ENABLED);
+
+ // We expect the provider to end up in PROVIDER_STATE_ENABLED, but it should have been
+ // disabled when the user changed.
+ // The controller should schedule a runnable to make a suggestion if the provider doesn't
+ // send a success event.
+ int[] expectedStateTransitions = { PROVIDER_STATE_DISABLED, PROVIDER_STATE_ENABLED };
+ mTestLocationTimeZoneProvider.assertStateChangesAndCommit(expectedStateTransitions);
+ mTestLocationTimeZoneProvider.assertConfig(USER2_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ mTestCallback.assertNoSuggestionMade();
+
+ // Simulate no event being received, and time passing.
+ mTestThreadingDomain.executeNext();
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER2_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertUncertainSuggestionMadeAndCommit();
+ }
+
+ private static LocationTimeZoneEvent createLocationTimeZoneEvent(@UserIdInt int userId,
+ int eventType, @Nullable List<String> timeZoneIds) {
+ LocationTimeZoneEvent.Builder builder = new LocationTimeZoneEvent.Builder()
+ .setElapsedRealtimeNanos(ARBITRARY_TIME)
+ .setUserHandle(UserHandle.of(userId))
+ .setEventType(eventType);
+ if (timeZoneIds != null) {
+ builder.setTimeZoneIds(timeZoneIds);
+ }
+ return builder.build();
+ }
+
+ private static class TestEnvironment extends LocationTimeZoneProviderController.Environment {
+
+ private final LocationTimeZoneProviderController mController;
+ private ConfigurationInternal mConfigurationInternal;
+
+ TestEnvironment(ThreadingDomain threadingDomain,
+ LocationTimeZoneProviderController controller,
+ ConfigurationInternal configurationInternal) {
+ super(threadingDomain);
+ mController = Objects.requireNonNull(controller);
+ mConfigurationInternal = Objects.requireNonNull(configurationInternal);
+ }
+
+ @Override
+ ConfigurationInternal getCurrentUserConfigurationInternal() {
+ return mConfigurationInternal;
+ }
+
+ void simulateConfigChange(ConfigurationInternal newConfig) {
+ ConfigurationInternal oldConfig = mConfigurationInternal;
+ mConfigurationInternal = Objects.requireNonNull(newConfig);
+ if (Objects.equals(oldConfig, newConfig)) {
+ fail("Bad test? No config change when one was expected");
+ }
+ mController.onConfigChanged();
+ }
+ }
+
+ private static class TestCallback extends LocationTimeZoneProviderController.Callback {
+
+ private TestState<GeolocationTimeZoneSuggestion> mLatestSuggestion = new TestState<>();
+
+ TestCallback(ThreadingDomain threadingDomain) {
+ super(threadingDomain);
+ }
+
+ @Override
+ void suggest(GeolocationTimeZoneSuggestion suggestion) {
+ mLatestSuggestion.set(suggestion);
+ }
+
+ void assertSuggestionMadeAndCommit(@Nullable List<String> expectedZoneIds) {
+ mLatestSuggestion.assertHasBeenSet();
+ assertEquals(expectedZoneIds, mLatestSuggestion.getLatest().getZoneIds());
+ mLatestSuggestion.commitLatest();
+ }
+
+ void assertNoSuggestionMade() {
+ mLatestSuggestion.assertHasNotBeenSet();
+ }
+
+ void assertUncertainSuggestionMadeAndCommit() {
+ // An "uncertain" suggestion has null time zone IDs.
+ assertSuggestionMadeAndCommit(null);
+ }
+ }
+
+ private static class TestLocationTimeZoneProvider extends LocationTimeZoneProvider {
+
+ /** Used to track historic provider states for tests. */
+ private final TestState<ProviderState> mTestProviderState = new TestState<>();
+ private boolean mInitialized;
+
+ /**
+ * Creates the instance.
+ */
+ TestLocationTimeZoneProvider(ThreadingDomain threadingDomain, String providerName) {
+ super(threadingDomain, providerName);
+ }
+
+ @Override
+ void onInitialize() {
+ mInitialized = true;
+ }
+
+ @Override
+ void onSetCurrentState(ProviderState newState) {
+ mTestProviderState.set(newState);
+ }
+
+ @Override
+ void onEnable() {
+ // Nothing needed for tests.
+ }
+
+ @Override
+ void onDisable() {
+ // Nothing needed for tests.
+ }
+
+ @Override
+ void logWarn(String msg) {
+ System.out.println(msg);
+ }
+
+ @Override
+ public void dump(IndentingPrintWriter pw, String[] args) {
+ // Nothing needed for tests.
+ }
+
+ /** Asserts that {@link #initialize(ProviderListener)} has been called. */
+ void assertInitialized() {
+ assertTrue(mInitialized);
+ }
+
+ void assertIsDisabled() {
+ // Disabled providers don't hold config.
+ assertConfig(null);
+ assertIsEnabledAndCommit(false);
+ }
+
+ /**
+ * Asserts the provider's config matches the expected, and the current state is set
+ * accordinly. Commits the latest changes to the state.
+ */
+ void assertIsEnabled(@NonNull ConfigurationInternal expectedConfig) {
+ assertConfig(expectedConfig);
+
+ boolean expectIsEnabled = expectedConfig.getAutoDetectionEnabledBehavior();
+ assertIsEnabledAndCommit(expectIsEnabled);
+ }
+
+ private void assertIsEnabledAndCommit(boolean enabled) {
+ ProviderState currentState = mCurrentState.get();
+ if (enabled) {
+ assertEquals(PROVIDER_STATE_ENABLED, currentState.stateEnum);
+ } else {
+ assertEquals(PROVIDER_STATE_DISABLED, currentState.stateEnum);
+ }
+ mTestProviderState.commitLatest();
+ }
+
+ void assertConfig(@NonNull ConfigurationInternal expectedConfig) {
+ ProviderState currentState = mCurrentState.get();
+ assertEquals(expectedConfig, currentState.currentUserConfiguration);
+ }
+
+ void simulateLocationTimeZoneEvent(@NonNull LocationTimeZoneEvent event) {
+ handleLocationTimeZoneEvent(event);
+ }
+
+ /**
+ * Asserts the most recent state changes. The ordering is such that the last element in the
+ * provided array is expected to be the current state.
+ */
+ void assertStateChangesAndCommit(int... expectedProviderStates) {
+ if (expectedProviderStates.length == 0) {
+ mTestProviderState.assertHasNotBeenSet();
+ } else {
+ mTestProviderState.assertChangeCount(expectedProviderStates.length);
+
+ List<ProviderState> previousProviderStates = new ArrayList<>();
+ for (int i = 0; i < expectedProviderStates.length; i++) {
+ previousProviderStates.add(mTestProviderState.getPrevious(i));
+ }
+ // The loop above will produce a list with the most recent state in element 0. So,
+ // reverse the list as the arguments to this method are expected to be in order
+ // oldest...latest.
+ Collections.reverse(previousProviderStates);
+
+ boolean allMatch = true;
+ for (int i = 0; i < expectedProviderStates.length; i++) {
+ allMatch = allMatch && expectedProviderStates[i]
+ == previousProviderStates.get(i).stateEnum;
+ }
+ if (!allMatch) {
+ fail("Provider state enums expected=" + Arrays.toString(expectedProviderStates)
+ + " but states were"
+ + " actually=" + previousProviderStates);
+ }
+ }
+ mTestProviderState.commitLatest();
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/HandlerThreadingDomainTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/HandlerThreadingDomainTest.java
new file mode 100644
index 0000000..cbaf0f3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/HandlerThreadingDomainTest.java
@@ -0,0 +1,208 @@
+/*
+ * 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.location.timezone;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.location.timezone.ThreadingDomain.SingleRunnableQueue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/** Tests for {@link HandlerThreadingDomain}. */
+@Presubmit
+public class HandlerThreadingDomainTest {
+
+ private HandlerThread mHandlerThread;
+ private Handler mTestHandler;
+
+ @Before
+ public void setUp() {
+ mHandlerThread = new HandlerThread("HandlerThreadingDomainTest");
+ mHandlerThread.start();
+ mTestHandler = new Handler(mHandlerThread.getLooper());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mHandlerThread.quit();
+ mHandlerThread.join();
+ }
+
+ @Test
+ public void getLockObject() {
+ ThreadingDomain domain = new HandlerThreadingDomain(mTestHandler);
+ assertSame("LockObject must be consistent", domain.getLockObject(), domain.getLockObject());
+ }
+
+ @Test
+ public void assertCurrentThread() throws Exception {
+ ThreadingDomain domain = new HandlerThreadingDomain(mTestHandler);
+
+ // Expect an exception (current thread != handler thread)
+ try {
+ domain.assertCurrentThread();
+ fail("Expected exception");
+ } catch (RuntimeException expected) {
+ // Expected
+ }
+
+ // Expect no exception (current thread == handler thread)
+ AtomicBoolean exceptionThrown = new AtomicBoolean(true);
+ LatchedRunnable testCode = new LatchedRunnable(() -> {
+ domain.assertCurrentThread();
+ exceptionThrown.set(false);
+ });
+ mTestHandler.post(testCode);
+ testCode.assertCompletesWithin(60, TimeUnit.SECONDS);
+ assertFalse(exceptionThrown.get());
+ }
+
+ @Test
+ public void post() throws Exception {
+ ThreadingDomain domain = new HandlerThreadingDomain(mTestHandler);
+ AtomicBoolean ranOnExpectedThread = new AtomicBoolean(false);
+ LatchedRunnable testLogic = new LatchedRunnable(() -> {
+ ranOnExpectedThread.set(Thread.currentThread() == mTestHandler.getLooper().getThread());
+ });
+ domain.post(testLogic);
+ testLogic.assertCompletesWithin(60, TimeUnit.SECONDS);
+ assertTrue(ranOnExpectedThread.get());
+ }
+
+ @Test
+ public void postDelayed() throws Exception {
+ ThreadingDomain domain = new HandlerThreadingDomain(mTestHandler);
+
+ long beforeExecutionNanos = System.nanoTime();
+ AtomicBoolean ranOnExpectedThread = new AtomicBoolean(false);
+ LatchedRunnable testLogic = new LatchedRunnable(() -> {
+ ranOnExpectedThread.set(Thread.currentThread() == mTestHandler.getLooper().getThread());
+ });
+ domain.postDelayed(testLogic, 5000);
+
+ testLogic.assertCompletesWithin(60, TimeUnit.SECONDS);
+ assertTrue(ranOnExpectedThread.get());
+
+ long afterExecutionNanos = System.nanoTime();
+ assertTrue(afterExecutionNanos - beforeExecutionNanos >= TimeUnit.SECONDS.toNanos(5));
+ }
+
+ @Test
+ public void singleRunnableHandler_runSynchronously() throws Exception {
+ ThreadingDomain domain = new HandlerThreadingDomain(mTestHandler);
+ SingleRunnableQueue singleRunnableQueue = domain.createSingleRunnableQueue();
+
+ AtomicBoolean testPassed = new AtomicBoolean(false);
+ // Calls to SingleRunnableQueue must be made on the handler thread it is associated with,
+ // so this uses runWithScissors() to block until the lambda has completed.
+ mTestHandler.runWithScissors(() -> {
+ Thread testThread = Thread.currentThread();
+ CountDownLatch latch = new CountDownLatch(1);
+ singleRunnableQueue.runSynchronously(() -> {
+ assertSame(Thread.currentThread(), testThread);
+ latch.countDown();
+ });
+ assertTrue(awaitWithRuntimeException(latch, 60, TimeUnit.SECONDS));
+ testPassed.set(true);
+ }, TimeUnit.SECONDS.toMillis(60));
+ assertTrue(testPassed.get());
+ }
+
+ @Test
+ public void singleRunnableHandler_runDelayed() throws Exception {
+ ThreadingDomain domain = new HandlerThreadingDomain(mTestHandler);
+ SingleRunnableQueue singleRunnableQueue = domain.createSingleRunnableQueue();
+
+ long beforeExecutionNanos = System.nanoTime();
+
+ Runnable noOpRunnable = () -> {
+ // Deliberately do nothing
+ };
+ LatchedRunnable firstRunnable = new LatchedRunnable(noOpRunnable);
+ LatchedRunnable secondRunnable = new LatchedRunnable(noOpRunnable);
+
+ // Calls to SingleRunnableQueue must be made on the handler thread it is associated with,
+ // so this uses runWithScissors() to block until the runDelayedTestRunnable has completed.
+ Runnable runDelayedTestRunnable = () -> {
+ singleRunnableQueue.runDelayed(firstRunnable, TimeUnit.SECONDS.toMillis(10));
+
+ // The second runnable posted must clear the first.
+ singleRunnableQueue.runDelayed(secondRunnable, TimeUnit.SECONDS.toMillis(10));
+ };
+ mTestHandler.runWithScissors(runDelayedTestRunnable, TimeUnit.SECONDS.toMillis(60));
+
+ // Now wait for the second runnable to complete
+ secondRunnable.assertCompletesWithin(60, TimeUnit.SECONDS);
+ assertFalse(firstRunnable.isComplete());
+
+ long afterExecutionNanos = System.nanoTime();
+ assertTrue(afterExecutionNanos - beforeExecutionNanos >= TimeUnit.SECONDS.toNanos(10));
+ }
+
+ private static boolean awaitWithRuntimeException(
+ CountDownLatch latch, long timeout, TimeUnit timeUnit) {
+ try {
+ return latch.await(timeout, timeUnit);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static class LatchedRunnable implements Runnable {
+
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+ private final Runnable mRunnable;
+
+ LatchedRunnable(Runnable mRunnable) {
+ this.mRunnable = Objects.requireNonNull(mRunnable);
+ }
+
+ @Override
+ public void run() {
+ try {
+ mRunnable.run();
+ } finally {
+ mLatch.countDown();
+ }
+ }
+
+ boolean isComplete() {
+ return mLatch.getCount() == 0;
+ }
+
+ boolean waitForCompletion(long timeout, TimeUnit unit) {
+ return awaitWithRuntimeException(mLatch, timeout, unit);
+ }
+
+ void assertCompletesWithin(long timeout, TimeUnit unit) {
+ assertTrue("Runnable did not execute in time", waitForCompletion(timeout, unit));
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/NullLocationTimeZoneProviderTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/NullLocationTimeZoneProviderTest.java
new file mode 100644
index 0000000..7c882fc
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/NullLocationTimeZoneProviderTest.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 com.android.server.location.timezone;
+
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import android.platform.test.annotations.Presubmit;
+import android.util.IndentingPrintWriter;
+
+import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState;
+import com.android.server.timezonedetector.ConfigurationInternal;
+import com.android.server.timezonedetector.TestState;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for {@link NullLocationTimeZoneProvider} and, indirectly, the class it extends
+ * {@link LocationTimeZoneProvider}.
+ */
+@Presubmit
+public class NullLocationTimeZoneProviderTest {
+
+ private TestThreadingDomain mTestThreadingDomain;
+
+ private TestController mTestController;
+
+ @Before
+ public void setUp() {
+ mTestThreadingDomain = new TestThreadingDomain();
+ mTestController = new TestController(mTestThreadingDomain);
+ }
+
+ @Test
+ public void initialization() {
+ String providerName = "primary";
+ NullLocationTimeZoneProvider provider =
+ new NullLocationTimeZoneProvider(mTestThreadingDomain, providerName);
+ provider.initialize(providerState -> mTestController.onProviderStateChange(providerState));
+
+ ProviderState currentState = provider.getCurrentState();
+ assertEquals(PROVIDER_STATE_DISABLED, currentState.stateEnum);
+ assertNull(currentState.currentUserConfiguration);
+ assertSame(provider, currentState.provider);
+ mTestThreadingDomain.assertQueueEmpty();
+ }
+
+ @Test
+ public void enableSchedulesPermFailure() {
+ String providerName = "primary";
+ NullLocationTimeZoneProvider provider =
+ new NullLocationTimeZoneProvider(mTestThreadingDomain, providerName);
+ provider.initialize(providerState -> mTestController.onProviderStateChange(providerState));
+
+ ConfigurationInternal config = USER1_CONFIG_GEO_DETECTION_ENABLED;
+ provider.enable(config);
+
+ // The StubbedProvider should enters enabled state, but immediately schedule a runnable to
+ // switch to perm failure.
+ ProviderState currentState = provider.getCurrentState();
+ assertSame(provider, currentState.provider);
+ assertEquals(PROVIDER_STATE_ENABLED, currentState.stateEnum);
+ assertEquals(config, currentState.currentUserConfiguration);
+ mTestThreadingDomain.assertSingleImmediateQueueItem();
+ // Entering enabled() does not trigger an onProviderStateChanged() as it is requested by the
+ // controller.
+ mTestController.assertProviderChangeNotTriggered();
+
+ // Check the queued runnable causes the provider to go into perm failed state.
+ mTestThreadingDomain.executeNext();
+
+ // Entering perm failed triggers an onProviderStateChanged() as it is asynchronously
+ // triggered.
+ mTestController.assertProviderChangeTriggered(PROVIDER_STATE_PERM_FAILED);
+ }
+
+ /** A test stand-in for the {@link LocationTimeZoneProviderController}. */
+ private static class TestController extends LocationTimeZoneProviderController {
+
+ private TestState<ProviderState> mProviderState = new TestState<>();
+
+ TestController(ThreadingDomain threadingDomain) {
+ super(threadingDomain);
+ }
+
+ @Override
+ void initialize(Environment environment, Callback callback) {
+ // Not needed for provider testing.
+ }
+
+ @Override
+ void onConfigChanged() {
+ // Not needed for provider testing.
+ }
+
+ void onProviderStateChange(ProviderState providerState) {
+ this.mProviderState.set(providerState);
+ }
+
+ @Override
+ public void dump(IndentingPrintWriter pw, String[] args) {
+ // Not needed for provider testing.
+ }
+
+ void assertProviderChangeTriggered(int expectedStateEnum) {
+ assertEquals(expectedStateEnum, mProviderState.getLatest().stateEnum);
+ mProviderState.commitLatest();
+ }
+
+ public void assertProviderChangeNotTriggered() {
+ mProviderState.assertHasNotBeenSet();
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java b/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java
new file mode 100644
index 0000000..192ade7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java
@@ -0,0 +1,53 @@
+/*
+ * 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.location.timezone;
+
+import android.annotation.UserIdInt;
+
+import com.android.server.timezonedetector.ConfigurationInternal;
+
+/** Shared test support code for this package. */
+final class TestSupport {
+ static final @UserIdInt int USER1_ID = 9999;
+
+ static final ConfigurationInternal USER1_CONFIG_GEO_DETECTION_ENABLED =
+ createUserConfig(USER1_ID, true);
+
+ static final ConfigurationInternal USER1_CONFIG_GEO_DETECTION_DISABLED =
+ createUserConfig(USER1_ID, false);
+
+ static final @UserIdInt int USER2_ID = 1234567890;
+
+ static final ConfigurationInternal USER2_CONFIG_GEO_DETECTION_ENABLED =
+ createUserConfig(USER2_ID, true);
+
+ static final ConfigurationInternal USER2_CONFIG_GEO_DETECTION_DISABLED =
+ createUserConfig(USER2_ID, false);
+
+ private TestSupport() {
+ }
+
+ private static ConfigurationInternal createUserConfig(
+ @UserIdInt int userId, boolean geoDetectionEnabled) {
+ return new ConfigurationInternal.Builder(userId)
+ .setUserConfigAllowed(true)
+ .setAutoDetectionSupported(true)
+ .setAutoDetectionEnabled(true)
+ .setLocationEnabled(true)
+ .setGeoDetectionEnabled(geoDetectionEnabled)
+ .build();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java b/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java
new file mode 100644
index 0000000..70ff22d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.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.location.timezone;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.time.Duration;
+import java.util.LinkedList;
+import java.util.Objects;
+
+/**
+ * A ThreadingDomain that simulates idealized post() semantics. Execution takes place in zero time,
+ * exactly when scheduled, when the test code explicitly requests it. Execution takes place on the
+ * test's main thread.
+ */
+class TestThreadingDomain extends ThreadingDomain {
+
+ static class QueuedRunnable {
+ @NonNull public final Runnable runnable;
+ @Nullable public final Object token;
+ public final long executionTimeMillis;
+
+ QueuedRunnable(@NonNull Runnable runnable, @Nullable Object token,
+ long executionTimeMillis) {
+ this.runnable = Objects.requireNonNull(runnable);
+ this.token = token;
+ this.executionTimeMillis = executionTimeMillis;
+ }
+
+ @Override
+ public String toString() {
+ return "QueuedRunnable{"
+ + "runnable=" + runnable
+ + ", token=" + token
+ + ", executionTimeMillis=" + executionTimeMillis
+ + '}';
+ }
+ }
+
+ private long mCurrentTimeMillis;
+ private LinkedList<QueuedRunnable> mQueue = new LinkedList<>();
+
+ TestThreadingDomain() {
+ // Pick an arbitrary time.
+ mCurrentTimeMillis = 123456L;
+ }
+
+ @Override
+ Thread getThread() {
+ return Thread.currentThread();
+ }
+
+ @Override
+ void post(Runnable r) {
+ mQueue.add(new QueuedRunnable(r, null, mCurrentTimeMillis));
+ }
+
+ @Override
+ void postDelayed(Runnable r, long delayMillis) {
+ mQueue.add(new QueuedRunnable(r, null, mCurrentTimeMillis + delayMillis));
+ }
+
+ @Override
+ void postDelayed(Runnable r, Object token, long delayMillis) {
+ mQueue.add(new QueuedRunnable(r, token, mCurrentTimeMillis + delayMillis));
+ }
+
+ @Override
+ void removeQueuedRunnables(Object token) {
+ mQueue.removeIf(runnable -> runnable.token != null && runnable.token.equals(token));
+ }
+
+ void assertSingleDelayedQueueItem(Duration expectedDelay) {
+ assertQueueLength(1);
+ assertNextQueueItemIsDelayed(expectedDelay);
+ }
+
+ void assertSingleImmediateQueueItem() {
+ assertQueueLength(1);
+ assertNextQueueItemIsImmediate();
+ }
+
+ void assertQueueLength(int expectedLength) {
+ assertEquals(expectedLength, mQueue.size());
+ }
+
+ void assertNextQueueItemIsImmediate() {
+ assertTrue(getNextQueueItemDelayMillis() == 0);
+ }
+
+ void assertNextQueueItemIsDelayed(Duration expectedDelay) {
+ assertTrue(getNextQueueItemDelayMillis() == expectedDelay.toMillis());
+ }
+
+ void assertQueueEmpty() {
+ assertTrue(mQueue.isEmpty());
+ }
+
+ long getNextQueueItemDelayMillis() {
+ assertQueueLength(1);
+ return mQueue.getFirst().executionTimeMillis - mCurrentTimeMillis;
+ }
+
+ void executeNext() {
+ assertQueueLength(1);
+
+ QueuedRunnable queued = mQueue.removeFirst();
+ mCurrentTimeMillis = queued.executionTimeMillis;
+ queued.runnable.run();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index 7d3ec03..a38745f 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -406,7 +406,7 @@
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
- // Enter test mode with whitelisted credentials
+ // Enter test mode with allowlisted credentials
when(mTestOnlyInsecureCertificateHelper.isTestOnlyCertificateAlias(any())).thenReturn(true);
when(mTestOnlyInsecureCertificateHelper.doesCredentialSupportInsecureMode(anyInt(), any()))
.thenReturn(true);
@@ -415,7 +415,7 @@
verify(mTestOnlyInsecureCertificateHelper)
.getDefaultCertificateAliasIfEmpty(eq(TEST_ROOT_CERT_ALIAS));
- // run whitelist checks
+ // run allowlist checks
verify(mTestOnlyInsecureCertificateHelper)
.doesCredentialSupportInsecureMode(anyInt(), any());
verify(mTestOnlyInsecureCertificateHelper)
@@ -424,7 +424,7 @@
KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
assertNotNull(keyChainSnapshot); // created snapshot
List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
- assertThat(applicationKeys).hasSize(0); // non whitelisted key is not included
+ assertThat(applicationKeys).hasSize(0); // non allowlisted key is not included
verify(mMockScrypt, never()).scrypt(any(), any(), anyInt(), anyInt(), anyInt(), anyInt());
}
@@ -437,7 +437,7 @@
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
- // Enter test mode with non whitelisted credentials
+ // Enter test mode with non allowlisted credentials
when(mTestOnlyInsecureCertificateHelper.isTestOnlyCertificateAlias(any())).thenReturn(true);
when(mTestOnlyInsecureCertificateHelper.doesCredentialSupportInsecureMode(anyInt(), any()))
.thenReturn(false);
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 58b71d4..f8043fa 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -1396,7 +1396,7 @@
actualCycleDay = mService.getCycleDayFromCarrierConfig(null, DEFAULT_CYCLE_DAY);
assertEquals(DEFAULT_CYCLE_DAY, actualCycleDay);
- // Sane, non-default values
+ // Valid, non-default values
assertCycleDayAsExpected(config, 1, true);
assertCycleDayAsExpected(config, cal.getMaximum(Calendar.DAY_OF_MONTH), true);
assertCycleDayAsExpected(config, cal.getMinimum(Calendar.DAY_OF_MONTH), true);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 1d384e9..7694d09 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -155,8 +155,8 @@
@Test
public void test_serializePackage() throws Exception {
try (PackageParser2 pp = PackageParser2.forParsingFileWithDefaults()) {
- ParsedPackage pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */,
- true /* useCaches */);
+ AndroidPackage pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */,
+ true /* useCaches */).hideAsFinal();
Parcel p = Parcel.obtain();
pkg.writeToParcel(p, 0 /* flags */);
@@ -467,7 +467,7 @@
ParsedInstrumentation b) {
assertComponentsEqual(a, b);
- // Sanity check for InstrumentationInfo.
+ // Validity check for InstrumentationInfo.
assertEquals(a.getTargetPackage(), b.getTargetPackage());
assertEquals(a.getTargetProcesses(), b.getTargetProcesses());
assertEquals(a.isHandleProfiling(), b.isHandleProfiling());
@@ -482,7 +482,7 @@
) {
assertComponentsEqual(a, b);
- // Sanity check for ServiceInfo.
+ // Validity check for ServiceInfo.
ServiceInfo aInfo = PackageInfoUtils.generateServiceInfo(aPkg, a, 0,
new PackageUserState(), 0, mockPkgSetting(aPkg));
ServiceInfo bInfo = PackageInfoUtils.generateServiceInfo(bPkg, b, 0,
@@ -509,7 +509,7 @@
) {
assertComponentsEqual(a, b);
- // Sanity check for ActivityInfo.
+ // Validity check for ActivityInfo.
ActivityInfo aInfo = PackageInfoUtils.generateActivityInfo(aPkg, a, 0,
new PackageUserState(), 0, mockPkgSetting(aPkg));
ActivityInfo bInfo = PackageInfoUtils.generateActivityInfo(bPkg, b, 0,
@@ -522,7 +522,7 @@
ParsedPermissionGroup b) {
assertComponentsEqual(a, b);
- // Sanity check for PermissionGroupInfo.
+ // Validity check for PermissionGroupInfo.
assertEquals(a.getName(), b.getName());
assertEquals(a.getDescriptionRes(), b.getDescriptionRes());
}
@@ -591,7 +591,7 @@
null
)
.setUse32BitAbi(true)
- .setVolumeUuid("foo3")
+ .setVolumeUuid("d52ef59a-7def-4541-bf21-4c28ed4b65a0")
.addPermission(permission)
.addPermissionGroup(new ParsedPermissionGroup())
.addActivity(new ParsedActivity())
@@ -665,13 +665,13 @@
}
if (List.class.isAssignableFrom(fieldType)) {
- // Sanity check for list fields: Assume they're non-null and contain precisely
+ // Validity check for list fields: Assume they're non-null and contain precisely
// one element.
List<?> list = (List<?>) f.get(pkg);
assertNotNull("List was null: " + f, list);
assertEquals(1, list.size());
} else if (fieldType.getComponentType() != null) {
- // Sanity check for array fields: Assume they're non-null and contain precisely
+ // Validity check for array fields: Assume they're non-null and contain precisely
// one element.
Object array = f.get(pkg);
assertNotNull(Array.get(array, 0));
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 4d8cc4647..1ca3c74 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -470,11 +470,16 @@
private PackageManagerService.ScanResult executeScan(
PackageManagerService.ScanRequest scanRequest) throws PackageManagerException {
- return PackageManagerService.scanPackageOnlyLI(
+ PackageManagerService.ScanResult result = PackageManagerService.scanPackageOnlyLI(
scanRequest,
mMockInjector,
false /*isUnderFactoryTest*/,
System.currentTimeMillis());
+
+ // Need to call hideAsFinal to cache derived fields. This is normally done in PMS, but not
+ // in this cut down flow used for the test.
+ ((ParsedPackage) result.pkgSetting.pkg).hideAsFinal();
+ return result;
}
private static String createCodePath(String packageName) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
index 87979fb..8e94544 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
@@ -150,7 +150,7 @@
ArrayMap<String, Set<String>> r = new ArrayMap<>();
r.put("com.android.package1", new ArraySet<>(Arrays.asList(
USER_TYPE_PROFILE_MANAGED, "invalid-garbage2")));
- // com.android.package2 has nothing blacklisted
+ // com.android.package2 has nothing denylisted
r.put("com.android.package3", new ArraySet<>(Arrays.asList("SYSTEM")));
return r;
}
@@ -196,7 +196,7 @@
/**
* Test that determineWhitelistedPackagesForUserTypes does not include packages that were never
- * whitelisted properly, but does include packages that were whitelisted but then blacklisted.
+ * allowlisted properly, but does include packages that were allowlisted but then denylisted.
*/
@Test
public void testDetermineWhitelistedPackagesForUserTypes_noNetWhitelisting() {
@@ -205,22 +205,22 @@
public ArrayMap<String, Set<String>> getAndClearPackageToUserTypeWhitelist() {
ArrayMap<String, Set<String>> r = new ArrayMap<>();
r.put("com.android.package1", new ArraySet<>(Arrays.asList("invalid1")));
- // com.android.package2 has no whitelisting
+ // com.android.package2 has no allowlisting
r.put("com.android.package3", new ArraySet<>(Arrays.asList("PROFILE", "FULL")));
r.put("com.android.package4", new ArraySet<>(Arrays.asList("PROFILE")));
r.put("com.android.package5", new ArraySet<>());
- // com.android.package6 has no whitelisting
+ // com.android.package6 has no allowlisting
return r;
}
@Override
public ArrayMap<String, Set<String>> getAndClearPackageToUserTypeBlacklist() {
ArrayMap<String, Set<String>> r = new ArrayMap<>();
- // com.android.package1 has no blacklisting
+ // com.android.package1 has no denylisting
r.put("com.android.package2", new ArraySet<>(Arrays.asList("FULL")));
r.put("com.android.package3", new ArraySet<>(Arrays.asList("PROFILE", "FULL")));
r.put("com.android.package4", new ArraySet<>(Arrays.asList("PROFILE", "invalid4")));
- // com.android.package5 has no blacklisting
+ // com.android.package5 has no denylisting
r.put("com.android.package6", new ArraySet<>(Arrays.asList("invalid6")));
return r;
}
@@ -242,18 +242,18 @@
*/
@Test
public void testShouldInstallPackage() {
- final String packageName1 = "pkg1"; // whitelisted
- final String packageName2 = "pkg2"; // whitelisted and blacklisted
- final String packageName3 = "pkg3"; // whitelisted for a different user type
- final String packageName4 = "pkg4"; // not whitelisted nor blacklisted at all
+ final String packageName1 = "pkg1"; // allowlisted
+ final String packageName2 = "pkg2"; // allowlisted and denylisted
+ final String packageName3 = "pkg3"; // allowlisted for a different user type
+ final String packageName4 = "pkg4"; // not allowlisted nor denylisted at all
- // Whitelist: user type bitset for each pkg (for the test, all that matters is 0 vs non-0).
+ // Allowlist: user type bitset for each pkg (for the test, all that matters is 0 vs non-0).
final ArrayMap<String, Long> pkgBitSetMap = new ArrayMap<>();
pkgBitSetMap.put(packageName1, 0b01L);
pkgBitSetMap.put(packageName2, 0L);
pkgBitSetMap.put(packageName3, 0b10L);
- // Whitelist of pkgs for this specific user, i.e. subset of pkgBitSetMap for this user.
+ // Allowlist of pkgs for this specific user, i.e. subset of pkgBitSetMap for this user.
final Set<String> userWhitelist = new ArraySet<>();
userWhitelist.add(packageName1);
@@ -266,7 +266,7 @@
final AndroidPackage pkg4 = ((ParsedPackage) PackageImpl.forTesting(packageName4)
.hideAsParsed()).hideAsFinal();
- // No implicit whitelist, so only install pkg1.
+ // No implicit allowlist, so only install pkg1.
boolean implicit = false;
assertTrue(UserSystemPackageInstaller.shouldInstallPackage(
pkg1, pkgBitSetMap, userWhitelist, implicit));
@@ -277,7 +277,7 @@
assertFalse(UserSystemPackageInstaller.shouldInstallPackage(
pkg4, pkgBitSetMap, userWhitelist, implicit));
- // Use implicit whitelist, so install pkg1 and pkg4
+ // Use implicit allowlist, so install pkg1 and pkg4
implicit = true;
assertTrue(UserSystemPackageInstaller.shouldInstallPackage(
pkg1, pkgBitSetMap, userWhitelist, implicit));
@@ -302,12 +302,12 @@
final long maskOfTypeA = 0b0001L;
final long maskOfTypeC = 0b0100L;
- final String packageName1 = "pkg1"; // whitelisted for user type A
- final String packageName2 = "pkg2"; // blacklisted whenever whitelisted
- final String packageName3 = "pkg3"; // whitelisted for user type C
- final String packageName4 = "pkg4"; // whitelisted for user type A
+ final String packageName1 = "pkg1"; // allowlisted for user type A
+ final String packageName2 = "pkg2"; // denylisted whenever allowlisted
+ final String packageName3 = "pkg3"; // allowlisted for user type C
+ final String packageName4 = "pkg4"; // allowlisted for user type A
- final ArrayMap<String, Long> pkgBitSetMap = new ArrayMap<>(); // Whitelist: bitset per pkg
+ final ArrayMap<String, Long> pkgBitSetMap = new ArrayMap<>(); // Allowlist: bitset per pkg
pkgBitSetMap.put(packageName1, maskOfTypeA);
pkgBitSetMap.put(packageName2, 0L);
pkgBitSetMap.put(packageName3, maskOfTypeC);
@@ -368,7 +368,7 @@
}
// Add auto-generated RRO package to expectedPackages since they are not (supposed to be)
- // in the whitelist but they should be installed.
+ // in the allowlist but they should be installed.
for (PackageInfo p : packageInfos) {
if (p.isOverlayPackage()
&& UserSystemPackageInstaller.hasAutoGeneratedRROSuffix(p.packageName)
@@ -396,7 +396,7 @@
}
/**
- * Test that overlay package not in whitelist should be installed for all user at Explicit mode.
+ * Test that overlay package not in allowlist should be installed for all user at Explicit mode.
*/
@Test
public void testInstallOverlayPackagesExplicitMode() {
@@ -513,7 +513,7 @@
assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode());
}
- /** Sets the whitelist mode to the desired value via adb's setprop. */
+ /** Sets the allowlist mode to the desired value via adb's setprop. */
private void setUserTypePackageWhitelistMode(int mode) {
UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
try {
@@ -529,7 +529,7 @@
/** @see UserSystemPackageInstaller#mWhitelistedPackagesForUserTypes */
private ArrayMap<String, Long> getNewPackageToWhitelistedBitSetMap() {
final ArrayMap<String, Long> pkgBitSetMap = new ArrayMap<>();
- // "android" is always treated as whitelisted for all types, regardless of the xml file.
+ // "android" is always treated as allowlisted for all types, regardless of the xml file.
pkgBitSetMap.put("android", ~0L);
return pkgBitSetMap;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index 8398585..ff43da6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -836,7 +836,7 @@
@Test
public void testOobPackageSelectionWhitelist() {
- // Various whitelist of apps to run in OOB mode.
+ // Various allowlist of apps to run in OOB mode.
final String kWhitelistApp0 = "com.priv.app0";
final String kWhitelistApp1 = "com.priv.app1";
final String kWhitelistApp2 = "com.priv.app2";
@@ -845,17 +845,17 @@
// Packages that shares the targeting process.
final Collection<String> runningPackages = Arrays.asList("com.priv.app1", "com.priv.app2");
- // Feature is off, whitelist does not matter
+ // Feature is off, allowlist does not matter
assertFalse(shouldPackageRunOob(false, kWhitelistApp0, runningPackages));
assertFalse(shouldPackageRunOob(false, kWhitelistApp1, runningPackages));
assertFalse(shouldPackageRunOob(false, "", runningPackages));
assertFalse(shouldPackageRunOob(false, "ALL", runningPackages));
- // Feature is on, app not in whitelist
+ // Feature is on, app not in allowlist
assertFalse(shouldPackageRunOob(true, kWhitelistApp0, runningPackages));
assertFalse(shouldPackageRunOob(true, "", runningPackages));
- // Feature is on, app in whitelist
+ // Feature is on, app in allowlist
assertTrue(shouldPackageRunOob(true, kWhitelistApp1, runningPackages));
assertTrue(shouldPackageRunOob(true, kWhitelistApp2, runningPackages));
assertTrue(shouldPackageRunOob(true, kWhitelistApp1AndApp2, runningPackages));
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt
index d36dcce..fb82b1d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt
@@ -59,7 +59,9 @@
fun appInfo(flag: Int, fieldFunction: (ApplicationInfo) -> List<Any?>) = Param(
flag, ApplicationInfo::class.java.simpleName,
- ::oldAppInfo, ::newAppInfo, fieldFunction
+ { pkg, flags -> oldAppInfo(pkg, flags) },
+ { pkg, flags -> newAppInfo(pkg, flags) },
+ fieldFunction
)
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
index 0f5f0af..61252c4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
@@ -32,6 +32,7 @@
import android.os.Bundle
import android.os.Debug
import android.os.Environment
+import android.os.Process
import android.util.SparseArray
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.pm.PackageManagerService
@@ -54,7 +55,7 @@
private const val VERIFY_ALL_APKS = true
- /** For auditing memory usage differences */
+ // For auditing memory usage differences to /sdcard/AndroidPackageParsingTestBase.hprof
private const val DUMP_HPROF_TO_EXTERNAL = false
val context: Context = InstrumentationRegistry.getInstrumentation().getContext()
@@ -104,10 +105,12 @@
@JvmStatic
@BeforeClass
fun setUpPackages() {
+ var uid = Process.FIRST_APPLICATION_UID
apks.mapNotNull {
try {
packageParser.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, false) to
- packageParser2.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, false)
+ packageParser2.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR,
+ false)
} catch (ignored: Exception) {
// It is intentional that a failure of either call here will result in failing
// both. Having null on one side would mean nothing to compare. Due to the
@@ -117,8 +120,15 @@
null
}
}.forEach { (old, new) ->
+ // Assign an arbitrary UID. This is normally done after parsing completes, inside
+ // PackageManagerService, but since that code isn't run here, need to mock it. This
+ // is equivalent to what the system would assign.
+ old.applicationInfo.uid = uid
+ new.uid = uid
+ uid++
+
oldPackages += old
- newPackages += new
+ newPackages += new.hideAsFinal()
}
if (DUMP_HPROF_TO_EXTERNAL) {
@@ -131,12 +141,29 @@
}
}
- fun oldAppInfo(pkg: PackageParser.Package, flags: Int = 0): ApplicationInfo? {
- return PackageParser.generateApplicationInfo(pkg, flags, dummyUserState, 0)
+ fun oldAppInfo(
+ pkg: PackageParser.Package,
+ flags: Int = 0,
+ userId: Int = 0
+ ): ApplicationInfo? {
+ return PackageParser.generateApplicationInfo(pkg, flags, dummyUserState, userId)
}
- fun newAppInfo(pkg: AndroidPackage, flags: Int = 0): ApplicationInfo? {
- return PackageInfoUtils.generateApplicationInfo(pkg, flags, dummyUserState, 0,
+ fun newAppInfo(
+ pkg: AndroidPackage,
+ flags: Int = 0,
+ userId: Int = 0
+ ): ApplicationInfo? {
+ return PackageInfoUtils.generateApplicationInfo(pkg, flags, dummyUserState, userId,
+ mockPkgSetting(pkg))
+ }
+
+ fun newAppInfoWithoutState(
+ pkg: AndroidPackage,
+ flags: Int = 0,
+ userId: Int = 0
+ ): ApplicationInfo? {
+ return PackageInfoUtils.generateApplicationInfo(pkg, flags, dummyUserState, userId,
mockPkgSetting(pkg))
}
@@ -152,6 +179,7 @@
private fun mockPkgSetting(aPkg: AndroidPackage) = mockThrowOnUnmocked<PackageSetting> {
this.pkg = aPkg
+ this.appId = aPkg.uid
whenever(pkgState) { PackageStateUnserialized() }
whenever(readUserState(anyInt())) { dummyUserState }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageInfoUserFieldsTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageInfoUserFieldsTest.kt
new file mode 100644
index 0000000..67b5d68
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageInfoUserFieldsTest.kt
@@ -0,0 +1,145 @@
+/*
+ * 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.pm.parsing
+
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageParser
+import android.os.Environment
+import android.os.UserHandle
+import android.platform.test.annotations.Presubmit
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Test
+
+/**
+ * As a performance optimization, the new parsing code builds the user data directories manually
+ * using string concatenation. This tries to mirror the logic that [Environment] uses, but it is
+ * still fragile to changes and potentially different device configurations.
+ *
+ * This compares the resultant values against the old [PackageParser] outputs as well as
+ * [ApplicationInfo]'s own [ApplicationInfo.initForUser].
+ */
+@Presubmit
+class PackageInfoUserFieldsTest : AndroidPackageParsingTestBase() {
+
+ @Test
+ fun userEnvironmentValues() {
+ // Specifically use a large user ID to test assumptions about single character IDs
+ val userId = 110
+
+ oldPackages.zip(newPackages)
+ .map { (old, new) ->
+ (old to oldAppInfo(pkg = old, userId = userId)!!) to
+ (new to newAppInfo(pkg = new, userId = userId)!!)
+ }
+ .forEach { (oldPair, newPair) ->
+ val (oldPkg, oldInfo) = oldPair
+ val (newPkg, newInfo) = newPair
+
+ val oldValuesActual = extractActual(oldInfo)
+ val newValuesActual = extractActual(newInfo)
+ val oldValuesExpected: Values
+ val newValuesExpected: Values
+
+ val packageName = oldPkg.packageName
+ if (packageName == "android") {
+ val systemDataDir = Environment.getDataSystemDirectory().absolutePath
+ oldValuesExpected = Values(
+ uid = UserHandle.getUid(userId,
+ UserHandle.getAppId(oldPkg.applicationInfo.uid)),
+ userDe = null,
+ userCe = null,
+ dataDir = systemDataDir
+ )
+ newValuesExpected = Values(
+ uid = UserHandle.getUid(userId, UserHandle.getAppId(newPkg.uid)),
+ userDe = null,
+ userCe = null,
+ dataDir = systemDataDir
+ )
+ } else {
+ oldValuesExpected = extractExpected(oldInfo, oldInfo.uid, userId)
+ newValuesExpected = extractExpected(newInfo, newPkg.uid, userId)
+ }
+
+ // Calls the internal ApplicationInfo logic to compare against. This must be
+ // done after saving the original values, since this will overwrite them.
+ oldInfo.initForUser(userId)
+ newInfo.initForUser(userId)
+
+ val oldInitValues = extractActual(oldInfo)
+ val newInitValues = extractActual(newInfo)
+
+ // The optimization is also done for the no state API that isn't used by the
+ // system. This API is still exposed publicly, so for this test we should
+ // verify it.
+ val newNoStateValues = extractActual(
+ newAppInfoWithoutState(newPkg, 0, userId)!!)
+
+ assertAllEquals(packageName,
+ oldValuesActual, oldValuesExpected, oldInitValues,
+ newValuesActual, newValuesExpected, newInitValues, newNoStateValues)
+ }
+ }
+
+ private fun assertAllEquals(packageName: String, vararg values: Values) {
+ // Local function to avoid accidentally calling wrong type
+ fun assertAllEquals(message: String, vararg values: Any?) {
+ values.forEachIndexed { index, value ->
+ if (index == 0) return@forEachIndexed
+ assertWithMessage("$message $index").that(values[0]).isEqualTo(value)
+ }
+ }
+
+ assertAllEquals("$packageName mismatched uid", values.map { it.uid })
+ assertAllEquals("$packageName mismatched userDe", values.map { it.userDe })
+ assertAllEquals("$packageName mismatched userCe", values.map { it.userCe })
+ assertAllEquals("$packageName mismatched dataDir", values.map { it.dataDir })
+ }
+
+ private fun extractActual(appInfo: ApplicationInfo) = Values(
+ uid = appInfo.uid,
+ userDe = appInfo.deviceProtectedDataDir,
+ userCe = appInfo.credentialProtectedDataDir,
+ dataDir = appInfo.dataDir
+ )
+
+ private fun extractExpected(appInfo: ApplicationInfo, appIdUid: Int, userId: Int): Values {
+ val userDe = Environment.getDataUserDePackageDirectory(appInfo.volumeUuid, userId,
+ appInfo.packageName).absolutePath
+ val userCe = Environment.getDataUserCePackageDirectory(appInfo.volumeUuid, userId,
+ appInfo.packageName).absolutePath
+ val dataDir = if (appInfo.isDefaultToDeviceProtectedStorage) {
+ appInfo.deviceProtectedDataDir
+ } else {
+ appInfo.credentialProtectedDataDir
+ }
+
+ return Values(
+ uid = UserHandle.getUid(userId, UserHandle.getAppId(appIdUid)),
+ userDe = userDe,
+ userCe = userCe,
+ dataDir = dataDir
+ )
+ }
+
+ data class Values(
+ val uid: Int,
+ val userDe: String?,
+ val userCe: String?,
+ val dataDir: String?
+ )
+}
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
new file mode 100644
index 0000000..3221a4d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -0,0 +1,271 @@
+/*
+ * 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 static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.server.SystemService;
+import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
+import com.android.server.powerstats.nano.PowerStatsServiceProto;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.util.Random;
+
+/**
+ * Tests for {@link com.android.server.powerstats.PowerStatsService}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:PowerStatsServiceTest
+ */
+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 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 final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ private PowerStatsService mService;
+ private File mDataStorageDir;
+ private TimerTrigger mTimerTrigger;
+ private PowerStatsLogger mPowerStatsLogger;
+
+ private final PowerStatsService.Injector mInjector = new PowerStatsService.Injector() {
+ @Override
+ File createDataStoragePath() {
+ mDataStorageDir = null;
+
+ try {
+ mDataStorageDir = Files.createTempDirectory(DATA_STORAGE_SUBDIR).toFile();
+ } catch (IOException e) {
+ fail("Could not create temp directory.");
+ }
+
+ return mDataStorageDir;
+ }
+
+ @Override
+ String createDataStorageFilename() {
+ return DATA_STORAGE_FILENAME;
+ }
+
+ @Override
+ IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() {
+ return new TestPowerStatsHALWrapper();
+ }
+
+ @Override
+ PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
+ String dataStorageFilename, IPowerStatsHALWrapper powerStatsHALWrapper) {
+ mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath, dataStorageFilename,
+ powerStatsHALWrapper);
+ return mPowerStatsLogger;
+ }
+
+ @Override
+ BatteryTrigger createBatteryTrigger(Context context, PowerStatsLogger powerStatsLogger) {
+ return new BatteryTrigger(context, powerStatsLogger, false /* trigger enabled */);
+ }
+
+ @Override
+ TimerTrigger createTimerTrigger(Context context, PowerStatsLogger powerStatsLogger) {
+ mTimerTrigger = new TimerTrigger(context, powerStatsLogger,
+ false /* trigger enabled */);
+ return mTimerTrigger;
+ }
+ };
+
+ 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);
+ }
+ return railInfoArray;
+ }
+
+ @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);
+ }
+ return energyDataArray;
+ }
+
+ @Override
+ public boolean initialize() {
+ return true;
+ }
+ }
+
+ @Before
+ public void setUp() {
+ mService = new PowerStatsService(mContext, mInjector);
+ }
+
+ @Test
+ public void testWrittenPowerStatsHALDataMatchesReadIncidentReportData()
+ 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.writeToFile(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);
+
+ // 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 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);
+ }
+ }
+
+ @Test
+ public void testCorruptOnDeviceStorage() 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, DATA_STORAGE_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.writeToFile(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);
+
+ // 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);
+ }
+
+ // No energyData should be written to the incident report since it
+ // is all corrupt (random bytes generated above).
+ assertTrue(pssProto.energyData.length == 0);
+ }
+
+ @Test
+ public void testNotEnoughBytesAfterLengthField() 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, DATA_STORAGE_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.writeToFile(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);
+
+ // 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);
+ }
+
+ // No energyData should be written to the incident report since the
+ // input buffer had only length and no data.
+ assertTrue(pssProto.energyData.length == 0);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java b/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java
index 13a3c2f..46224cb 100644
--- a/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java
@@ -123,7 +123,7 @@
JSONArray cacheSizes = output.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY);
assertThat(cacheSizes.length()).isEqualTo(2);
- // We need to do this crazy Set over this because the DiskStatsFileLogger provides no
+ // We need to do this unexpected Set over this because the DiskStatsFileLogger provides no
// guarantee of the ordering of the apps in its output. By using a set, we avoid any order
// problems.
ArraySet<AppSizeGrouping> apps = new ArraySet<>();
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index 10981ab..17d99e6 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -184,7 +184,7 @@
/**
* Tests that readPermissions works correctly with {@link SystemConfig#ALLOW_APP_CONFIGS}
- * permission flag for the tag: whitelisted-staged-installer.
+ * permission flag for the tag: allowlisted-staged-installer.
*/
@Test
public void readPermissions_allowAppConfigs_parsesStagedInstallerWhitelist()
@@ -204,7 +204,7 @@
/**
* Tests that readPermissions works correctly without {@link SystemConfig#ALLOW_APP_CONFIGS}
- * permission flag for the tag: whitelisted-staged-installer.
+ * permission flag for the tag: allowlisted-staged-installer.
*/
@Test
public void readPermissions_notAllowAppConfigs_wontParseStagedInstallerWhitelist()
diff --git a/services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java b/services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java
index a787c32..3b963f6 100644
--- a/services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/textclassifier/IconsContentProviderTest.java
@@ -29,7 +29,7 @@
import org.junit.runner.RunWith;
/**
- * Sanity test for {@link IconsContentProvider}.
+ * Validity test for {@link IconsContentProvider}.
*/
@RunWith(AndroidJUnit4.class)
public final class IconsContentProviderTest {
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java
index 7049efa1..97b8360 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java
@@ -76,7 +76,7 @@
/** Asserts the number of times {@link #set} has been called. */
public void assertChangeCount(int expectedCount) {
- assertEquals(expectedCount, mValues.size());
+ assertEquals(expectedCount, getChangeCount());
}
/**
@@ -89,4 +89,25 @@
}
return mInitialValue;
}
+
+ /** Returns the number of times {@link #set} has been called. */
+ public int getChangeCount() {
+ return mValues.size();
+ }
+
+ /**
+ * Returns an historic value of the state. Values for {@code age} can be from {@code 0}, the
+ * latest value, through {@code getChangeCount() - 1}, which returns the oldest change, to
+ * {@code getChangeCount()}, which returns the initial value. Values outside of this range will
+ * cause {@link IndexOutOfBoundsException} to be thrown.
+ */
+ public T getPrevious(int age) {
+ int size = mValues.size();
+ if (age < size) {
+ return mValues.get(size - 1 - age);
+ } else if (age == size) {
+ return mInitialValue;
+ }
+ throw new IndexOutOfBoundsException("age=" + age + " is too big.");
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 2aeab20..2c64526 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -112,6 +112,8 @@
private static final int UID_SYSTEM_HEADFULL = 10002;
private static final String PACKAGE_SYSTEM_HEADLESS = "com.example.system.headless";
private static final int UID_SYSTEM_HEADLESS = 10003;
+ private static final String PACKAGE_WELLBEING = "com.example.wellbeing";
+ private static final int UID_WELLBEING = 10004;
private static final int USER_ID = 0;
private static final int USER_ID2 = 10;
private static final UserHandle USER_HANDLE_USER2 = new UserHandle(USER_ID2);
@@ -221,6 +223,11 @@
}
@Override
+ boolean isWellbeingPackage(String packageName) {
+ return PACKAGE_WELLBEING.equals(packageName);
+ }
+
+ @Override
void updatePowerWhitelistCache() {
}
@@ -332,6 +339,12 @@
pish.packageName = PACKAGE_SYSTEM_HEADLESS;
packages.add(pish);
+ PackageInfo piw = new PackageInfo();
+ piw.applicationInfo = new ApplicationInfo();
+ piw.applicationInfo.uid = UID_WELLBEING;
+ piw.packageName = PACKAGE_WELLBEING;
+ packages.add(piw);
+
doReturn(packages).when(mockPm).getInstalledPackagesAsUser(anyInt(), anyInt());
try {
for (int i = 0; i < packages.size(); ++i) {
@@ -1520,6 +1533,25 @@
assertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
}
+ @Test
+ public void testWellbeingAppElevated() {
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_WELLBEING);
+ assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_WELLBEING);
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+ assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
+
+ // Make sure the default wellbeing app does not get lowered below WORKING_SET.
+ mController.setAppStandbyBucket(PACKAGE_WELLBEING, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_TIMEOUT);
+ assertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_WELLBEING);
+
+ // A non default wellbeing app should be able to fall lower than WORKING_SET.
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_TIMEOUT);
+ assertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
+ }
+
private String getAdminAppsStr(int userId) {
return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId));
}
diff --git a/services/tests/servicestests/utils/com/android/server/testutils/TestUtils.java b/services/tests/servicestests/utils/com/android/server/testutils/TestUtils.java
index b200293..fa14b24 100644
--- a/services/tests/servicestests/utils/com/android/server/testutils/TestUtils.java
+++ b/services/tests/servicestests/utils/com/android/server/testutils/TestUtils.java
@@ -52,7 +52,7 @@
* EasyMock-style "strict" mock that throws immediately on any interaction that was not
* explicitly allowed.
*
- * You can allow certain method calls on a whitelist basis by stubbing them e.g. with
+ * You can allow certain method calls on a allowlist basis by stubbing them e.g. with
* {@link Mockito#doAnswer}, {@link Mockito#doNothing}, etc.
*/
public static <T> T strictMock(Class<T> c) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 94e4041..9511181 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -124,7 +124,7 @@
spyOn(mNavBarWindow);
// Disabling this call for most tests since it can override the systemUiFlags when called.
- doReturn(0).when(mDisplayPolicy).updateSystemUiVisibilityLw();
+ doReturn(false).when(mDisplayPolicy).updateSystemUiVisibilityLw();
updateDisplayFrames();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index b77d21c..a55423a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -19,9 +19,9 @@
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.Surface.ROTATION_0;
-import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -78,8 +78,7 @@
attrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
attrs.format = PixelFormat.OPAQUE;
- attrs.systemUiVisibility = attrs.subtreeSystemUiVisibility = win.mSystemUiVisibility =
- hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0;
+ attrs.insetsFlags.appearance = hasLightNavBar ? APPEARANCE_LIGHT_NAVIGATION_BARS : 0;
return win;
}
@@ -103,8 +102,7 @@
attrs.flags = FLAG_NOT_FOCUSABLE | FLAG_LAYOUT_IN_SCREEN
| (drawNavBar ? FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS : 0);
attrs.format = PixelFormat.TRANSPARENT;
- attrs.systemUiVisibility = attrs.subtreeSystemUiVisibility = win.mSystemUiVisibility =
- hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0;
+ attrs.insetsFlags.appearance = hasLightNavBar ? APPEARANCE_LIGHT_NAVIGATION_BARS : 0;
win.mHasSurface = visible;
return win;
}
@@ -163,6 +161,7 @@
@Test
public void testUpdateLightNavigationBarLw() {
+ DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
final WindowState opaqueDarkNavBar = createOpaqueFullscreen(false);
final WindowState opaqueLightNavBar = createOpaqueFullscreen(true);
@@ -171,50 +170,50 @@
final WindowState imeDrawDarkNavBar = createInputMethodWindow(true, true, false);
final WindowState imeDrawLightNavBar = createInputMethodWindow(true, true, true);
- assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
- DisplayPolicy.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null,
+ assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS,
+ displayPolicy.updateLightNavigationBarLw(
+ APPEARANCE_LIGHT_NAVIGATION_BARS, null, null,
null, null));
- // Opaque top fullscreen window overrides SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR flag.
- assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+ // Opaque top fullscreen window overrides APPEARANCE_LIGHT_NAVIGATION_BARS flag.
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(
0, opaqueDarkNavBar, opaqueDarkNavBar, null, opaqueDarkNavBar));
- assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, opaqueDarkNavBar, null,
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(
+ APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueDarkNavBar, opaqueDarkNavBar, null,
opaqueDarkNavBar));
- assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
- DisplayPolicy.updateLightNavigationBarLw(0, opaqueLightNavBar,
+ assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS,
+ displayPolicy.updateLightNavigationBarLw(0, opaqueLightNavBar,
opaqueLightNavBar, null, opaqueLightNavBar));
- assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
- DisplayPolicy.updateLightNavigationBarLw(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS,
+ displayPolicy.updateLightNavigationBarLw(APPEARANCE_LIGHT_NAVIGATION_BARS,
opaqueLightNavBar, opaqueLightNavBar, null, opaqueLightNavBar));
- // Dimming window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.
- assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+ // Dimming window clears APPEARANCE_LIGHT_NAVIGATION_BARS.
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(
0, opaqueDarkNavBar, dimming, null, dimming));
- assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(
0, opaqueLightNavBar, dimming, null, dimming));
- assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, dimming, null, dimming));
- assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, null, dimming));
- assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, imeDrawLightNavBar,
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(
+ APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueDarkNavBar, dimming, null, dimming));
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(
+ APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar, dimming, null, dimming));
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(
+ APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar, dimming, imeDrawLightNavBar,
dimming));
- // IME window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
- assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null, imeDrawDarkNavBar,
+ // IME window clears APPEARANCE_LIGHT_NAVIGATION_BARS
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(
+ APPEARANCE_LIGHT_NAVIGATION_BARS, null, null, imeDrawDarkNavBar,
imeDrawDarkNavBar));
- // Even if the top fullscreen has SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, IME window wins.
- assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, opaqueLightNavBar,
+ // Even if the top fullscreen has APPEARANCE_LIGHT_NAVIGATION_BARS, IME window wins.
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(
+ APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar, opaqueLightNavBar,
imeDrawDarkNavBar, imeDrawDarkNavBar));
- // IME window should be able to use SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.
- assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
- DisplayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar,
+ // IME window should be able to use APPEARANCE_LIGHT_NAVIGATION_BARS.
+ assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS,
+ displayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar,
opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index af8cb02..94ffcda 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -45,7 +45,6 @@
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
-import android.view.View;
import android.view.WindowManagerGlobal;
import com.android.internal.R;
@@ -99,11 +98,9 @@
mStatusBarWindow.mAttrs.gravity = Gravity.TOP;
addWindow(mStatusBarWindow);
- mDisplayPolicy.mLastSystemUiFlags |= View.STATUS_BAR_TRANSPARENT;
mNavBarWindow.mAttrs.gravity = Gravity.BOTTOM;
addWindow(mNavBarWindow);
- mDisplayPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT;
// Update source frame and visibility of insets providers.
mDisplayContent.getInsetsStateController().onPostLayout();
diff --git a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java
index 56cb447..f53894a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java
@@ -102,7 +102,7 @@
final FakeDeviceConfig config = new FakeDeviceConfig();
mBlacklist = new HighRefreshRateBlacklist(r, config);
- // First check that the default blacklist is in effect
+ // First check that the default denylist is in effect
assertTrue(mBlacklist.isBlacklisted(APP1));
assertFalse(mBlacklist.isBlacklisted(APP2));
assertFalse(mBlacklist.isBlacklisted(APP3));
@@ -130,7 +130,7 @@
assertTrue(mBlacklist.isBlacklisted(APP2));
assertTrue(mBlacklist.isBlacklisted(APP3));
- // Change an unrelated flag in our namespace and verify that the blacklist is intact
+ // Change an unrelated flag in our namespace and verify that the denylist is intact
config.putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, "someKey", "someValue");
assertFalse(mBlacklist.isBlacklisted(APP1));
assertTrue(mBlacklist.isBlacklisted(APP2));
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 edcf0d4..f154073 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -566,7 +566,7 @@
assertEquals(new Rect(mActivity.getBounds().left, 0, dh - mActivity.getBounds().right, 0),
mActivity.getLetterboxInsets());
- final StatusBarController statusBarController =
+ final BarController statusBarController =
mActivity.mDisplayContent.getDisplayPolicy().getStatusBarController();
// The activity doesn't fill the display, so the letterbox of the rotated activity is
// overlapped with the rotated content frame of status bar. Hence the status bar shouldn't
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
index ca3f815..a54bbaf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -219,19 +219,13 @@
activity.setDetachedFromWindowLatch(onDetachedFromWindowLatch);
final int id = activity.getTaskId();
- // Test for onTaskCreated.
- waitForCallback(taskCreatedLaunchLatch);
+ // Test for onTaskCreated and onTaskMovedToFront
+ waitForCallback(taskMovedToFrontLatch);
+ assertEquals(0, taskCreatedLaunchLatch.getCount());
assertEquals(id, params[0]);
ComponentName componentName = (ComponentName) params[1];
assertEquals(ActivityTaskChangeCallbacks.class.getName(), componentName.getClassName());
- // Test for onTaskMovedToFront.
- assertEquals(1, taskMovedToFrontLatch.getCount());
- mService.moveTaskToFront(null, getInstrumentation().getContext().getPackageName(), id, 0,
- null);
- waitForCallback(taskMovedToFrontLatch);
- assertEquals(activity.getTaskId(), params[0]);
-
// Test for onTaskRemovalStarted.
assertEquals(1, taskRemovalStartedLatch.getCount());
assertEquals(1, taskRemovedLatch.getCount());
@@ -609,8 +603,7 @@
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mActivityView = new ActivityView(this, null /* attrs */, 0 /* defStyle */,
- true /* singleTaskInstance */);
+ mActivityView = new ActivityView.Builder(this).setSingleInstance(true).build();
setContentView(mActivityView);
ViewGroup.LayoutParams layoutParams = mActivityView.getLayoutParams();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 2510385..5b7cf5a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -27,6 +27,8 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+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.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -164,4 +166,14 @@
verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId);
}
+
+ @Test
+ public void testDismissKeyguardCanWakeUp() {
+ doReturn(true).when(mWm).checkCallingPermission(anyString(), anyString());
+ spyOn(mWm.mAtmInternal);
+ doReturn(true).when(mWm.mAtmInternal).isDreaming();
+ doNothing().when(mWm.mAtmService.mStackSupervisor).wakeUp(anyString());
+ mWm.dismissKeyguard(null, "test-dismiss-keyguard");
+ verify(mWm.mAtmService.mStackSupervisor).wakeUp(anyString());
+ }
}
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index bebbbd0..f7fe1ba 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.annotation.SystemApi;
import android.media.ToneGenerator;
import android.os.Parcel;
import android.os.Parcelable;
@@ -80,21 +81,26 @@
* Reason code (returned via {@link #getReason()}) which indicates that a call could not be
* completed because the cellular radio is off or out of service, the device is connected to
* a wifi network, but the user has not enabled wifi calling.
- * @hide
*/
public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
/**
* Reason code (returned via {@link #getReason()}), which indicates that the video telephony
* call was disconnected because IMS access is blocked.
- * @hide
*/
public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
/**
- * Reason code, which indicates that the conference call is simulating single party conference.
+ * Reason code (returned via {@link #getReason()}), which indicates that the connection service
+ * is setting the call's state to {@link Call#STATE_DISCONNECTED} because it is internally
+ * changing the representation of an IMS conference call to simulate a single-party call.
+ *
+ * This reason code is only used for communication between a {@link ConnectionService} and
+ * Telecom and should not be surfaced to the user.
+ *
* @hide
*/
+ @SystemApi
public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
/**
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 768c8ee..c20e5ad 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -48,11 +48,15 @@
public final class PhoneAccount implements Parcelable {
/**
- * {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which determines the
- * sort order for {@link PhoneAccount}s from the same
- * {@link android.telecom.ConnectionService}.
+ * String extra which determines the order in which {@link PhoneAccount}s are sorted
+ *
+ * This is an extras key set via {@link Builder#setExtras} which determines the order in which
+ * {@link PhoneAccount}s from the same {@link ConnectionService} are sorted. The accounts
+ * are sorted by this key via standard lexicographical order, and this ordering is used to
+ * determine priority when a call can be placed via multiple accounts.
* @hide
*/
+ @SystemApi
public static final String EXTRA_SORT_ORDER =
"android.telecom.extra.SORT_ORDER";
@@ -85,8 +89,7 @@
/**
* Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which
* indicates that all calls from this {@link PhoneAccount} should be treated as VoIP calls
- * rather than cellular calls.
- * @hide
+ * rather than cellular calls by the Telecom audio handling logic.
*/
public static final String EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE =
"android.telecom.extra.ALWAYS_USE_VOIP_AUDIO_MODE";
@@ -160,6 +163,7 @@
* in progress.
* @hide
*/
+ @SystemApi
public static final String EXTRA_PLAY_CALL_RECORDING_TONE =
"android.telecom.extra.PLAY_CALL_RECORDING_TONE";
@@ -254,6 +258,7 @@
* See {@link #getCapabilities}
* @hide
*/
+ @SystemApi
public static final int CAPABILITY_EMERGENCY_CALLS_ONLY = 0x80;
/**
@@ -277,6 +282,7 @@
* convert all outgoing video calls to emergency numbers to audio-only.
* @hide
*/
+ @SystemApi
public static final int CAPABILITY_EMERGENCY_VIDEO_CALLING = 0x200;
/**
@@ -323,9 +329,9 @@
/**
* Flag indicating that this {@link PhoneAccount} is the preferred SIM subscription for
- * emergency calls. A {@link PhoneAccount} that sets this capabilitiy must also
+ * emergency calls. A {@link PhoneAccount} that sets this capability must also
* set the {@link #CAPABILITY_SIM_SUBSCRIPTION} and {@link #CAPABILITY_PLACE_EMERGENCY_CALLS}
- * capabilities. There should only be one emergency preferred {@link PhoneAccount}.
+ * capabilities. There must only be one emergency preferred {@link PhoneAccount} on the device.
* <p>
* When set, Telecom will prefer this {@link PhoneAccount} over others for emergency calling,
* even if the emergency call was placed with a specific {@link PhoneAccount} set using the
@@ -334,6 +340,7 @@
*
* @hide
*/
+ @SystemApi
public static final int CAPABILITY_EMERGENCY_PREFERRED = 0x2000;
/**
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 464f318..f1deec6 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -322,6 +322,7 @@
* the remote handle of the new call.
* @hide
*/
+ @SystemApi
public static final String EXTRA_UNKNOWN_CALL_HANDLE =
"android.telecom.extra.UNKNOWN_CALL_HANDLE";
@@ -392,6 +393,7 @@
* </ul>
* @hide
*/
+ @SystemApi
public static final String EXTRA_CALL_TECHNOLOGY_TYPE =
"android.telecom.extra.CALL_TECHNOLOGY_TYPE";
@@ -728,17 +730,57 @@
public static final int TTY_MODE_VCO = 3;
/**
- * Broadcast intent action indicating that the current TTY mode has changed. An intent extra
- * provides this state as an int.
+ * Broadcast intent action indicating that the current TTY mode has changed.
*
- * @see #EXTRA_CURRENT_TTY_MODE
+ * This intent will contain {@link #EXTRA_CURRENT_TTY_MODE} as an intent extra, giving the new
+ * TTY mode.
* @hide
*/
+ @TestApi
+ @SystemApi
public static final String ACTION_CURRENT_TTY_MODE_CHANGED =
"android.telecom.action.CURRENT_TTY_MODE_CHANGED";
/**
- * The lookup key for an int that indicates the current TTY mode.
+ * Integer extra key that indicates the current TTY mode.
+ *
+ * Used with {@link #ACTION_CURRENT_TTY_MODE_CHANGED}.
+ *
+ * Valid modes are:
+ * <ul>
+ * <li>{@link #TTY_MODE_OFF}</li>
+ * <li>{@link #TTY_MODE_FULL}</li>
+ * <li>{@link #TTY_MODE_HCO}</li>
+ * <li>{@link #TTY_MODE_VCO}</li>
+ * </ul>
+ *
+ * This TTY mode is distinct from the one sent via {@link #ACTION_TTY_PREFERRED_MODE_CHANGED},
+ * since the current TTY mode will always be {@link #TTY_MODE_OFF}unless a TTY terminal is
+ * plugged into the device.
+ * @hide
+ */
+ @TestApi
+ @SystemApi
+ public static final String EXTRA_CURRENT_TTY_MODE =
+ "android.telecom.extra.CURRENT_TTY_MODE";
+
+ /**
+ * Broadcast intent action indicating that the TTY preferred operating mode has changed.
+ *
+ * This intent will contain {@link #EXTRA_TTY_PREFERRED_MODE} as an intent extra, giving the new
+ * preferred TTY mode.
+ * @hide
+ */
+ @TestApi
+ @SystemApi
+ public static final String ACTION_TTY_PREFERRED_MODE_CHANGED =
+ "android.telecom.action.TTY_PREFERRED_MODE_CHANGED";
+
+ /**
+ * Integer extra key that indicates the preferred TTY mode.
+ *
+ * Used with {@link #ACTION_TTY_PREFERRED_MODE_CHANGED}.
+ *
* Valid modes are:
* <ul>
* <li>{@link #TTY_MODE_OFF}</li>
@@ -748,26 +790,8 @@
* </ul>
* @hide
*/
- public static final String EXTRA_CURRENT_TTY_MODE =
- "android.telecom.extra.CURRENT_TTY_MODE";
-
- /**
- * Broadcast intent action indicating that the TTY preferred operating mode has changed. An
- * intent extra provides the new mode as an int.
- *
- * @see #EXTRA_TTY_PREFERRED_MODE
- * @hide
- */
- public static final String ACTION_TTY_PREFERRED_MODE_CHANGED =
- "android.telecom.action.TTY_PREFERRED_MODE_CHANGED";
-
- /**
- * The lookup key for an int that indicates preferred TTY mode. Valid modes are: -
- * {@link #TTY_MODE_OFF} - {@link #TTY_MODE_FULL} - {@link #TTY_MODE_HCO} -
- * {@link #TTY_MODE_VCO}
- *
- * @hide
- */
+ @TestApi
+ @SystemApi
public static final String EXTRA_TTY_PREFERRED_MODE =
"android.telecom.extra.TTY_PREFERRED_MODE";
@@ -843,8 +867,10 @@
* {@link TelecomManager#CALL_SOURCE_EMERGENCY_DIALPAD},
* {@link TelecomManager#CALL_SOURCE_EMERGENCY_SHORTCUT}.
*
+ * Intended for use with the platform emergency dialer only.
* @hide
*/
+ @SystemApi
public static final String EXTRA_CALL_SOURCE = "android.telecom.extra.CALL_SOURCE";
/**
@@ -852,6 +878,7 @@
*
* @hide
*/
+ @SystemApi
public static final int CALL_SOURCE_EMERGENCY_SHORTCUT = 2;
/**
@@ -859,6 +886,7 @@
*
* @hide
*/
+ @SystemApi
public static final int CALL_SOURCE_EMERGENCY_DIALPAD = 1;
/**
@@ -866,6 +894,7 @@
*
* @hide
*/
+ @SystemApi
public static final int CALL_SOURCE_UNSPECIFIED = 0;
/**
diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt
index 72e11c8..ec8e8e7 100644
--- a/telephony/api/system-current.txt
+++ b/telephony/api/system-current.txt
@@ -888,6 +888,7 @@
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();
@@ -899,6 +900,11 @@
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 = 2; // 0x2
+ field public static final int HANDOVER_FAILURE_MODE_LEGACY = 1; // 0x1
+ field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER = 3; // 0x3
+ field public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 4; // 0x4
+ field public static final int HANDOVER_FAILURE_MODE_UNKNOWN = 0; // 0x0
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
@@ -912,6 +918,7 @@
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);
diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
index d4308c4..cfc7ac1 100644
--- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -157,7 +157,8 @@
List<String> enabledCarrierPackages = new ArrayList<>();
int carrierAppsHandledSdk =
- Settings.Secure.getInt(contentResolver, Settings.Secure.CARRIER_APPS_HANDLED, 0);
+ Settings.Secure.getIntForUser(contentResolver, Settings.Secure.CARRIER_APPS_HANDLED,
+ 0, contentResolver.getUserId());
if (DEBUG) {
Log.i(TAG, "Last execution SDK: " + carrierAppsHandledSdk);
}
@@ -309,8 +310,8 @@
// Mark the execution so we do not disable apps again on this SDK version.
if (!hasRunEver || !hasRunForSdk) {
- Settings.Secure.putInt(contentResolver, Settings.Secure.CARRIER_APPS_HANDLED,
- Build.VERSION.SDK_INT);
+ Settings.Secure.putIntForUser(contentResolver, Settings.Secure.CARRIER_APPS_HANDLED,
+ Build.VERSION.SDK_INT, contentResolver.getUserId());
}
if (!enabledCarrierPackages.isEmpty()) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 4de1abf..829c746 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -32,6 +32,7 @@
import android.service.carrier.CarrierService;
import android.telecom.TelecomManager;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsSsData;
import com.android.internal.telephony.ICarrierConfigLoader;
import com.android.telephony.Rlog;
@@ -66,6 +67,18 @@
public static final String EXTRA_SUBSCRIPTION_INDEX =
SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX;
+ /**
+ * Service class flag if not specify a service class.
+ * Reference: 3GPP TS 27.007 Section 7.4 Facility lock +CLCK
+ */
+ public static final int SERVICE_CLASS_NONE = ImsSsData.SERVICE_CLASS_NONE;
+
+ /**
+ * Service class flag for voice telephony.
+ * Reference: 3GPP TS 27.007 Section 7.4 Facility lock +CLCK
+ */
+ public static final int SERVICE_CLASS_VOICE = ImsSsData.SERVICE_CLASS_VOICE;
+
private final Context mContext;
/**
@@ -212,6 +225,18 @@
"call_barring_supports_deactivate_all_bool";
/**
+ * Specifies the service class for call barring service. Default value is
+ * {@link #SERVICE_CLASS_VOICE}.
+ * The value set as below:
+ * <ul>
+ * <li>0: {@link #SERVICE_CLASS_NONE}</li>
+ * <li>1: {@link #SERVICE_CLASS_VOICE}</li>
+ * </ul>
+ */
+ public static final String KEY_CALL_BARRING_DEFAULT_SERVICE_CLASS_INT =
+ "call_barring_default_service_class_int";
+
+ /**
* Flag indicating whether the Phone app should ignore EVENT_SIM_NETWORK_LOCKED
* events from the Sim.
* If true, this will prevent the IccNetworkDepersonalizationPanel from being shown, and
@@ -3889,10 +3914,23 @@
* Indicating whether DUN APN should be disabled when the device is roaming. In that case,
* the default APN (i.e. internet) will be used for tethering.
*
+ * This config is only available when using Preset APN(not user edited) as Preferred APN.
+ *
* @hide
*/
- public static final String KEY_DISABLE_DUN_APN_WHILE_ROAMING =
- "disable_dun_apn_while_roaming";
+ public static final String KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL =
+ "disable_dun_apn_while_roaming_with_preset_apn_bool";
+
+ /**
+ * Where there is no preferred APN, specifies the carrier's default preferred APN.
+ * Specifies the {@link android.provider.Telephony.Carriers.APN} of the default preferred apn.
+ *
+ * This config is only available with Preset APN(not user edited).
+ *
+ * @hide
+ */
+ public static final String KEY_DEFAULT_PREFERRED_APN_NAME_STRING =
+ "default_preferred_apn_name_string";
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -3968,6 +4006,7 @@
sDefaults.putBoolean(KEY_CALL_BARRING_VISIBILITY_BOOL, false);
sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL, true);
sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL, true);
+ sDefaults.putInt(KEY_CALL_BARRING_DEFAULT_SERVICE_CLASS_INT, SERVICE_CLASS_VOICE);
sDefaults.putBoolean(KEY_CALL_FORWARDING_VISIBILITY_BOOL, true);
sDefaults.putBoolean(KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL, true);
sDefaults.putBoolean(KEY_CALL_FORWARDING_WHEN_UNANSWERED_SUPPORTED_BOOL, true);
@@ -4432,7 +4471,8 @@
"ims:2", "cbs:2", "ia:2", "emergency:2", "mcx:3", "xcap:3"
});
sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]);
- sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING, false);
+ sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
+ sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 969016b..559a774 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -306,6 +306,8 @@
private static boolean sServiceHandleCacheEnabled = true;
@GuardedBy("sCacheLock")
+ private static ITelephony sITelephony;
+ @GuardedBy("sCacheLock")
private static IPhoneSubInfo sIPhoneSubInfo;
@GuardedBy("sCacheLock")
private static ISub sISub;
@@ -4180,7 +4182,7 @@
}
}
- /**
+ /**
* @param keyAvailability bitmask that defines the availabilty of keys for a type.
* @param keyType the key type which is being checked. (WLAN, EPDG)
* @return true if the digit at position keyType is 1, else false.
@@ -5499,13 +5501,39 @@
}
}
- /**
- * @hide
- */
+ /**
+ * @hide
+ */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private ITelephony getITelephony() {
- return ITelephony.Stub.asInterface(TelephonyFrameworkInitializer
- .getTelephonyServiceManager().getTelephonyServiceRegisterer().get());
+ // Keeps cache disabled until test fixes are checked into AOSP.
+ if (!sServiceHandleCacheEnabled) {
+ return ITelephony.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyServiceRegisterer()
+ .get());
+ }
+
+ if (sITelephony == null) {
+ ITelephony temp = ITelephony.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyServiceRegisterer()
+ .get());
+ synchronized (sCacheLock) {
+ if (sITelephony == null && temp != null) {
+ try {
+ sITelephony = temp;
+ sITelephony.asBinder().linkToDeath(sServiceDeath, 0);
+ } catch (Exception e) {
+ // something has gone horribly wrong
+ sITelephony = null;
+ }
+ }
+ }
+ }
+ return sITelephony;
}
private IOns getIOns() {
@@ -13401,6 +13429,10 @@
*/
private static void resetServiceCache() {
synchronized (sCacheLock) {
+ if (sITelephony != null) {
+ sITelephony.asBinder().unlinkToDeath(sServiceDeath, 0);
+ sITelephony = null;
+ }
if (sISub != null) {
sISub.asBinder().unlinkToDeath(sServiceDeath, 0);
sISub = null;
@@ -13417,9 +13449,9 @@
}
}
- /**
- * @hide
- */
+ /**
+ * @hide
+ */
static IPhoneSubInfo getSubscriberInfoService() {
// Keeps cache disabled until test fixes are checked into AOSP.
if (!sServiceHandleCacheEnabled) {
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index 242c2e9..5ead8de 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -67,6 +67,47 @@
/** Indicates the data connection is active with physical link up. */
public static final int LINK_STATUS_ACTIVE = 2;
+ /** {@hide} */
+ @IntDef(prefix = "HANDOVER_FAILURE_MODE_", value = {
+ HANDOVER_FAILURE_MODE_LEGACY,
+ HANDOVER_FAILURE_MODE_DO_FALLBACK,
+ HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER,
+ HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface HandoverFailureMode {}
+
+ /**
+ * Data handover failure mode is unknown.
+ */
+ public static final int HANDOVER_FAILURE_MODE_UNKNOWN = 0;
+
+ /**
+ * Perform fallback to the source data transport on data handover failure using
+ * the legacy logic, which is fallback if the fail cause is
+ * {@link DataFailCause#HANDOFF_PREFERENCE_CHANGED}.
+ */
+ public static final int HANDOVER_FAILURE_MODE_LEGACY = 1;
+
+ /**
+ * Perform fallback to the source data transport on data handover failure.
+ */
+ public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 2;
+
+ /**
+ * Do not perform fallback to the source data transport on data handover failure.
+ * Frameworks should keep retrying handover by sending
+ * {@link DataService#REQUEST_REASON_HANDOVER} request to the underlying {@link DataService}.
+ */
+ public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER = 3;
+
+ /**
+ * Do not perform fallback to the source transport on data handover failure.
+ * Frameworks should retry setup a new data connection by sending
+ * {@link DataService#REQUEST_REASON_NORMAL} request to the underlying {@link DataService}.
+ */
+ public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 4;
+
private final @DataFailureCause int mCause;
private final int mSuggestedRetryTime;
private final int mId;
@@ -80,6 +121,7 @@
private final int mMtu;
private final int mMtuV4;
private final int mMtuV6;
+ private final @HandoverFailureMode int mHandoverFailureMode;
/**
* @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
@@ -126,14 +168,15 @@
mPcscfAddresses = (pcscfAddresses == null)
? new ArrayList<>() : new ArrayList<>(pcscfAddresses);
mMtu = mMtuV4 = mMtuV6 = mtu;
+ mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY;
}
- /** @hide */
private DataCallResponse(@DataFailureCause int cause, int suggestedRetryTime, int id,
@LinkStatus int linkStatus, @ProtocolType int protocolType,
@Nullable String interfaceName, @Nullable List<LinkAddress> addresses,
@Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses,
- @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6) {
+ @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6,
+ @HandoverFailureMode int handoverFailureMode) {
mCause = cause;
mSuggestedRetryTime = suggestedRetryTime;
mId = id;
@@ -151,6 +194,7 @@
mMtu = mtu;
mMtuV4 = mtuV4;
mMtuV6 = mtuV6;
+ mHandoverFailureMode = handoverFailureMode;
}
/** @hide */
@@ -173,6 +217,7 @@
mMtu = source.readInt();
mMtuV4 = source.readInt();
mMtuV6 = source.readInt();
+ mHandoverFailureMode = source.readInt();
}
/**
@@ -262,6 +307,13 @@
return mMtuV6;
}
+ /**
+ * @return The data handover failure mode.
+ */
+ public @HandoverFailureMode int getHandoverFailureMode() {
+ return mHandoverFailureMode;
+ }
+
@NonNull
@Override
public String toString() {
@@ -339,6 +391,7 @@
dest.writeInt(mMtu);
dest.writeInt(mMtuV4);
dest.writeInt(mMtuV6);
+ dest.writeInt(mHandoverFailureMode);
}
public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR =
@@ -395,6 +448,8 @@
private int mMtuV6;
+ private @HandoverFailureMode int mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY;
+
/**
* Default constructor for Builder.
*/
@@ -553,6 +608,17 @@
}
/**
+ * Set data handover failure mode for the data call response.
+ *
+ * @param failureMode Handover failure mode.
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setHandoverFailureMode(@HandoverFailureMode int failureMode) {
+ mHandoverFailureMode = failureMode;
+ return this;
+ }
+
+ /**
* Build the DataCallResponse.
*
* @return the DataCallResponse object.
@@ -560,7 +626,7 @@
public @NonNull DataCallResponse build() {
return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
- mPcscfAddresses, mMtu, mMtuV4, mMtuV6);
+ mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode);
}
}
}
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 2b3072e..da7311c 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -137,18 +137,30 @@
}
@Override
- public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) {
- return createMmTelFeatureInternal(slotId, c);
+ public IImsMmTelFeature createMmTelFeature(int slotId) {
+ return createMmTelFeatureInternal(slotId);
}
@Override
- public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
- return createRcsFeatureInternal(slotId, c);
+ public IImsRcsFeature createRcsFeature(int slotId) {
+ return createRcsFeatureInternal(slotId);
}
@Override
- public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c) {
- ImsService.this.removeImsFeature(slotId, featureType, c);
+ public void addFeatureStatusCallback(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ ImsService.this.addImsFeatureStatusCallback(slotId, featureType, c);
+ }
+
+ @Override
+ public void removeFeatureStatusCallback(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ ImsService.this.removeImsFeatureStatusCallback(slotId, featureType, c);
+ }
+
+ @Override
+ public void removeImsFeature(int slotId, int featureType) {
+ ImsService.this.removeImsFeature(slotId, featureType);
}
@Override
@@ -204,11 +216,10 @@
return mFeaturesBySlot.get(slotId);
}
- private IImsMmTelFeature createMmTelFeatureInternal(int slotId,
- IImsFeatureStatusCallback c) {
+ private IImsMmTelFeature createMmTelFeatureInternal(int slotId) {
MmTelFeature f = createMmTelFeature(slotId);
if (f != null) {
- setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL, c);
+ setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL);
return f.getBinder();
} else {
Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
@@ -216,11 +227,10 @@
}
}
- private IImsRcsFeature createRcsFeatureInternal(int slotId,
- IImsFeatureStatusCallback c) {
+ private IImsRcsFeature createRcsFeatureInternal(int slotId) {
RcsFeature f = createRcsFeature(slotId);
if (f != null) {
- setupFeature(f, slotId, ImsFeature.FEATURE_RCS, c);
+ setupFeature(f, slotId, ImsFeature.FEATURE_RCS);
return f.getBinder();
} else {
Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned.");
@@ -228,13 +238,45 @@
}
}
- private void setupFeature(ImsFeature f, int slotId, int featureType,
- IImsFeatureStatusCallback c) {
+ private void setupFeature(ImsFeature f, int slotId, int featureType) {
f.initialize(this, slotId);
- f.addImsFeatureStatusCallback(c);
addImsFeature(slotId, featureType, f);
}
+ private void addImsFeatureStatusCallback(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ synchronized (mFeaturesBySlot) {
+ // get ImsFeature associated with the slot/feature
+ SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+ if (features == null) {
+ Log.w(LOG_TAG, "Can not add ImsFeatureStatusCallback - no features on slot "
+ + slotId);
+ return;
+ }
+ ImsFeature f = features.get(featureType);
+ if (f != null) {
+ f.addImsFeatureStatusCallback(c);
+ }
+ }
+ }
+
+ private void removeImsFeatureStatusCallback(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ synchronized (mFeaturesBySlot) {
+ // get ImsFeature associated with the slot/feature
+ SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+ if (features == null) {
+ Log.w(LOG_TAG, "Can not remove ImsFeatureStatusCallback - no features on slot "
+ + slotId);
+ return;
+ }
+ ImsFeature f = features.get(featureType);
+ if (f != null) {
+ f.removeImsFeatureStatusCallback(c);
+ }
+ }
+ }
+
private void addImsFeature(int slotId, int featureType, ImsFeature f) {
synchronized (mFeaturesBySlot) {
// Get SparseArray for Features, by querying slot Id
@@ -248,8 +290,7 @@
}
}
- private void removeImsFeature(int slotId, int featureType,
- IImsFeatureStatusCallback c) {
+ private void removeImsFeature(int slotId, int featureType) {
synchronized (mFeaturesBySlot) {
// get ImsFeature associated with the slot/feature
SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
@@ -264,7 +305,6 @@
+ featureType + " exists on slot " + slotId);
return;
}
- f.removeImsFeatureStatusCallback(c);
f.onFeatureRemoved();
features.remove(featureType);
}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
index c7da681..c956cbc 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
@@ -31,12 +31,14 @@
*/
interface IImsServiceController {
void setListener(IImsServiceControllerListener l);
- IImsMmTelFeature createMmTelFeature(int slotId, in IImsFeatureStatusCallback c);
- IImsRcsFeature createRcsFeature(int slotId, in IImsFeatureStatusCallback c);
+ IImsMmTelFeature createMmTelFeature(int slotId);
+ IImsRcsFeature createRcsFeature(int slotId);
ImsFeatureConfiguration querySupportedImsFeatures();
+ void addFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c);
+ void removeFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c);
// Synchronous call to ensure the ImsService is ready before continuing with feature creation.
void notifyImsServiceReadyForFeatureCreation();
- void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c);
+ void removeImsFeature(int slotId, int featureType);
IImsConfig getConfig(int slotId);
IImsRegistration getRegistration(int slotId);
oneway void enableIms(int slotId);
diff --git a/telephony/java/android/telephony/ims/compat/ImsService.java b/telephony/java/android/telephony/ims/compat/ImsService.java
index eafbb14..41d1d72 100644
--- a/telephony/java/android/telephony/ims/compat/ImsService.java
+++ b/telephony/java/android/telephony/ims/compat/ImsService.java
@@ -21,7 +21,6 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.os.IBinder;
-import android.os.RemoteException;
import android.telephony.CarrierConfigManager;
import android.telephony.ims.compat.feature.ImsFeature;
import android.telephony.ims.compat.feature.MMTelFeature;
@@ -91,25 +90,35 @@
protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
@Override
- public IImsMMTelFeature createEmergencyMMTelFeature(int slotId,
+ public IImsMMTelFeature createEmergencyMMTelFeature(int slotId) {
+ return createEmergencyMMTelFeatureInternal(slotId);
+ }
+
+ @Override
+ public IImsMMTelFeature createMMTelFeature(int slotId) {
+ return createMMTelFeatureInternal(slotId);
+ }
+
+ @Override
+ public IImsRcsFeature createRcsFeature(int slotId) {
+ return createRcsFeatureInternal(slotId);
+ }
+
+ @Override
+ public void removeImsFeature(int slotId, int featureType) {
+ ImsService.this.removeImsFeature(slotId, featureType);
+ }
+
+ @Override
+ public void addFeatureStatusCallback(int slotId, int featureType,
IImsFeatureStatusCallback c) {
- return createEmergencyMMTelFeatureInternal(slotId, c);
+ addImsFeatureStatusCallback(slotId, featureType, c);
}
@Override
- public IImsMMTelFeature createMMTelFeature(int slotId, IImsFeatureStatusCallback c) {
- return createMMTelFeatureInternal(slotId, c);
- }
-
- @Override
- public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
- return createRcsFeatureInternal(slotId, c);
- }
-
- @Override
- public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
- throws RemoteException {
- ImsService.this.removeImsFeature(slotId, featureType, c);
+ public void removeFeatureStatusCallback(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ removeImsFeatureStatusCallback(slotId, featureType, c);
}
};
@@ -137,46 +146,40 @@
return mFeaturesBySlot.get(slotId);
}
- private IImsMMTelFeature createEmergencyMMTelFeatureInternal(int slotId,
- IImsFeatureStatusCallback c) {
+ private IImsMMTelFeature createEmergencyMMTelFeatureInternal(int slotId) {
MMTelFeature f = onCreateEmergencyMMTelImsFeature(slotId);
if (f != null) {
- setupFeature(f, slotId, ImsFeature.EMERGENCY_MMTEL, c);
+ setupFeature(f, slotId, ImsFeature.EMERGENCY_MMTEL);
return f.getBinder();
} else {
return null;
}
}
- private IImsMMTelFeature createMMTelFeatureInternal(int slotId,
- IImsFeatureStatusCallback c) {
+ private IImsMMTelFeature createMMTelFeatureInternal(int slotId) {
MMTelFeature f = onCreateMMTelImsFeature(slotId);
if (f != null) {
- setupFeature(f, slotId, ImsFeature.MMTEL, c);
+ setupFeature(f, slotId, ImsFeature.MMTEL);
return f.getBinder();
} else {
return null;
}
}
- private IImsRcsFeature createRcsFeatureInternal(int slotId,
- IImsFeatureStatusCallback c) {
+ private IImsRcsFeature createRcsFeatureInternal(int slotId) {
RcsFeature f = onCreateRcsFeature(slotId);
if (f != null) {
- setupFeature(f, slotId, ImsFeature.RCS, c);
+ setupFeature(f, slotId, ImsFeature.RCS);
return f.getBinder();
} else {
return null;
}
}
- private void setupFeature(ImsFeature f, int slotId, int featureType,
- IImsFeatureStatusCallback c) {
+ private void setupFeature(ImsFeature f, int slotId, int featureType) {
f.setContext(this);
f.setSlotId(slotId);
- f.addImsFeatureStatusCallback(c);
addImsFeature(slotId, featureType, f);
- // TODO: Remove once new onFeatureReady AIDL is merged in.
f.onFeatureReady();
}
@@ -193,12 +196,45 @@
}
}
- private void removeImsFeature(int slotId, int featureType,
+ private void addImsFeatureStatusCallback(int slotId, int featureType,
IImsFeatureStatusCallback c) {
synchronized (mFeaturesBySlot) {
// get ImsFeature associated with the slot/feature
SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
if (features == null) {
+ Log.w(LOG_TAG, "Can not add ImsFeatureStatusCallback. No ImsFeatures exist on"
+ + " slot " + slotId);
+ return;
+ }
+ ImsFeature f = features.get(featureType);
+ if (f != null) {
+ f.addImsFeatureStatusCallback(c);
+ }
+ }
+ }
+
+ private void removeImsFeatureStatusCallback(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ synchronized (mFeaturesBySlot) {
+ // get ImsFeature associated with the slot/feature
+ SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+ if (features == null) {
+ Log.w(LOG_TAG, "Can not remove ImsFeatureStatusCallback. No ImsFeatures exist on"
+ + " slot " + slotId);
+ return;
+ }
+ ImsFeature f = features.get(featureType);
+ if (f != null) {
+ f.removeImsFeatureStatusCallback(c);
+ }
+ }
+ }
+
+ private void removeImsFeature(int slotId, int featureType) {
+ synchronized (mFeaturesBySlot) {
+ // get ImsFeature associated with the slot/feature
+ SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+ if (features == null) {
Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot "
+ slotId);
return;
@@ -209,7 +245,6 @@
+ featureType + " exists on slot " + slotId);
return;
}
- f.removeImsFeatureStatusCallback(c);
f.onFeatureRemoved();
features.remove(featureType);
}
diff --git a/telephony/java/com/android/ims/internal/IImsServiceController.aidl b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
index 857089f..e9528f4 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceController.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
@@ -25,8 +25,10 @@
* {@hide}
*/
interface IImsServiceController {
- IImsMMTelFeature createEmergencyMMTelFeature(int slotId, in IImsFeatureStatusCallback c);
- IImsMMTelFeature createMMTelFeature(int slotId, in IImsFeatureStatusCallback c);
- IImsRcsFeature createRcsFeature(int slotId, in IImsFeatureStatusCallback c);
- void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c);
+ IImsMMTelFeature createEmergencyMMTelFeature(int slotId);
+ IImsMMTelFeature createMMTelFeature(int slotId);
+ IImsRcsFeature createRcsFeature(int slotId);
+ void removeImsFeature(int slotId, int featureType);
+ void addFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c);
+ void removeFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c);
}
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index a53ea16..d430db5 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -29,7 +29,8 @@
"flickerlib",
"truth-prebuilt",
"launcher-helper-lib",
- "launcher-aosp-tapl"
+ "launcher-aosp-tapl",
+ "platform-test-annotations",
],
}
@@ -50,6 +51,7 @@
"truth-prebuilt",
"app-helpers-core",
"launcher-helper-lib",
- "launcher-aosp-tapl"
+ "launcher-aosp-tapl",
+ "platform-test-annotations",
],
}
\ No newline at end of file
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 b64811b..c1ba21a 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
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.ime
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.dsl.flicker
@@ -39,6 +40,7 @@
* Test IME window closing back to app window transitions.
* To run this test: `atest FlickerTests:CloseImeWindowToAppTest`
*/
+@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 0940c19..2c00722 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.ime
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.NonRotationTestBase
@@ -39,6 +40,7 @@
* Test IME window closing back to app window transitions.
* To run this test: `atest FlickerTests:CloseImeWindowToAppTest`
*/
+@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
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 c2e87db..4697adc 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
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.ime
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.NonRotationTestBase
@@ -40,6 +41,7 @@
* Test IME window closing to home transitions.
* To run this test: `atest FlickerTests:CloseImeWindowToHomeTest`
*/
+@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index 11ccb69..2caa8f3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.ime
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.NonRotationTestBase
@@ -39,6 +40,7 @@
* Test IME window opening transitions.
* To run this test: `atest FlickerTests:OpenImeWindowTest`
*/
+@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
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 1759072..2c9c8ba 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
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.launch
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.dsl.flicker
@@ -38,6 +39,7 @@
* Test cold launch app from launcher.
* To run this test: `atest FlickerTests:OpenAppColdTest`
*/
+@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
index 87c8633..7447bda 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.splitscreen
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.NonRotationTestBase
@@ -43,6 +44,7 @@
* Test open app to split screen.
* To run this test: `atest FlickerTests:SplitScreenToLauncherTest`
*/
+@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java
index 3bc5309..73e0163 100644
--- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java
+++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java
@@ -22,12 +22,11 @@
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.view.Gravity;
-import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
-import android.view.SurfaceControlViewHost;
import android.widget.Button;
import android.widget.FrameLayout;
@@ -46,8 +45,6 @@
mView.setZOrderOnTop(true);
mView.getHolder().addCallback(this);
-
- addEmbeddedView();
}
void addEmbeddedView() {
@@ -63,6 +60,8 @@
v.setBackgroundColor(Color.RED);
}
});
+ v.getViewTreeObserver().addOnWindowFocusChangeListener(focused ->
+ v.setBackgroundColor(focused ? Color.MAGENTA : Color.DKGRAY));
WindowManager.LayoutParams lp =
new WindowManager.LayoutParams(500, 500, WindowManager.LayoutParams.TYPE_APPLICATION,
0, PixelFormat.OPAQUE);
@@ -71,6 +70,7 @@
@Override
public void surfaceCreated(SurfaceHolder holder) {
+ addEmbeddedView();
}
@Override
diff --git a/tools/aapt2/DominatorTree_test.cpp b/tools/aapt2/DominatorTree_test.cpp
index fe4f951..3e49034 100644
--- a/tools/aapt2/DominatorTree_test.cpp
+++ b/tools/aapt2/DominatorTree_test.cpp
@@ -173,4 +173,30 @@
EXPECT_EQ(expected, printer.ToString(&tree));
}
+TEST(DominatorTreeTest, NonZeroDensitiesMatch) {
+ const ConfigDescription sw600_config = test::ParseConfigOrDie("sw600dp");
+ const ConfigDescription sw600_hdpi_config = test::ParseConfigOrDie("sw600dp-hdpi");
+ const ConfigDescription sw800_hdpi_config = test::ParseConfigOrDie("sw800dp-hdpi");
+ const ConfigDescription sw800_xxhdpi_config = test::ParseConfigOrDie("sw800dp-xxhdpi");
+
+ std::vector<std::unique_ptr<ResourceConfigValue>> configs;
+ configs.push_back(util::make_unique<ResourceConfigValue>(ConfigDescription::DefaultConfig(), ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(sw600_config, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(sw600_hdpi_config, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(sw800_hdpi_config, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(sw800_xxhdpi_config, ""));
+
+ DominatorTree tree(configs);
+ PrettyPrinter printer;
+
+ std::string expected =
+ "<default>\n"
+ " sw600dp-v13\n"
+ " sw600dp-hdpi-v13\n"
+ " sw800dp-hdpi-v13\n"
+ " sw800dp-xxhdpi-v13\n";
+ EXPECT_EQ(expected, printer.ToString(&tree));
+}
+
+
} // namespace aapt
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index ae01170..b78f48c 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -96,8 +96,6 @@
return "styleable";
case ResourceType::kTransition:
return "transition";
- case ResourceType::kUnknown:
- return "unknown";
case ResourceType::kXml:
return "xml";
}
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 22e667b..8fe0eb3 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -66,11 +66,6 @@
kStyle,
kStyleable,
kTransition,
-
- // Not a parsed type. It is only used when loading resource tables that may have modified type
- // names
- kUnknown,
-
kXml,
};
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index d84ca3d..118a76f 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -2334,11 +2334,15 @@
}
// Populate some default no-compress extensions that are already compressed.
- options_.extensions_to_not_compress.insert(
- {".jpg", ".jpeg", ".png", ".gif", ".wav", ".mp2", ".mp3", ".ogg",
- ".aac", ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", ".rtttl",
- ".imy", ".xmf", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2",
- ".3gpp2", ".amr", ".awb", ".wma", ".wmv", ".webm", ".mkv"});
+ options_.extensions_to_not_compress.insert({
+ // Image extensions
+ ".jpg", ".jpeg", ".png", ".gif", ".webp",
+ // Audio extensions
+ ".wav", ".mp2", ".mp3", ".ogg", ".aac", ".mid", ".midi", ".smf", ".jet", ".rtttl", ".imy",
+ ".xmf", ".amr", ".awb",
+ // Audio/video extensions
+ ".mpg", ".mpeg", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", ".wma", ".wmv",
+ ".webm", ".mkv"});
// Turn off auto versioning for static-libs.
if (context.GetPackageType() == PackageType::kStaticLib) {
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index f362744..cccd9fa 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -352,15 +352,15 @@
config.copyFromDtoH(type->config);
const std::string type_str = util::GetString(type_pool_, type->id - 1);
-
- // Be lenient on the name of the type if the table is lenient on resource validation.
- auto parsed_type = ResourceType::kUnknown;
- if (const ResourceType* parsed = ParseResourceType(type_str)) {
- parsed_type = *parsed;
- } else if (table_->GetValidateResources()) {
- diag_->Error(DiagMessage(source_) << "invalid type name '" << type_str << "' for type with ID "
- << (int) type->id);
- return false;
+ const ResourceType* parsed_type = ParseResourceType(type_str);
+ if (!parsed_type) {
+ // Be lenient on the name of the type if the table is lenient on resource validation.
+ bool log_error = table_->GetValidateResources();
+ if (log_error) {
+ diag_->Error(DiagMessage(source_) << "invalid type name '" << type_str
+ << "' for type with ID " << type->id);
+ }
+ return !log_error;
}
TypeVariant tv(type);
@@ -370,9 +370,8 @@
continue;
}
- const ResourceName name(package->name, parsed_type,
+ const ResourceName name(package->name, *parsed_type,
util::GetString(key_pool_, util::DeviceToHost32(entry->key.index)));
-
const ResourceId res_id(package->id.value(), type->id, static_cast<uint16_t>(it.index()));
std::unique_ptr<Value> resource_value;
diff --git a/tools/powerstats/Android.bp b/tools/powerstats/Android.bp
new file mode 100644
index 0000000..af41144
--- /dev/null
+++ b/tools/powerstats/Android.bp
@@ -0,0 +1,10 @@
+java_binary_host {
+ name: "PowerStatsServiceProtoParser",
+ manifest: "PowerStatsServiceProtoParser_manifest.txt",
+ srcs: [
+ "*.java",
+ ],
+ static_libs: [
+ "platformprotos",
+ ],
+}
diff --git a/tools/powerstats/PowerStatsServiceProtoParser.java b/tools/powerstats/PowerStatsServiceProtoParser.java
new file mode 100644
index 0000000..8ab302a
--- /dev/null
+++ b/tools/powerstats/PowerStatsServiceProtoParser.java
@@ -0,0 +1,95 @@
+/*
+ * 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 java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * This class implements a utility to parse ODPM data out
+ * of incident reports contained in bugreports. The data
+ * is output to STDOUT in csv format.
+ */
+public class PowerStatsServiceProtoParser {
+ private static void printRailInfo(PowerStatsServiceProto 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() + ",";
+ }
+ System.out.println(csvHeader);
+ }
+
+ private static void printEnergyData(PowerStatsServiceProto proto) {
+ int railInfoCount = proto.getRailInfoCount();
+
+ if (railInfoCount > 0) {
+ int energyDataCount = proto.getEnergyDataCount();
+ int energyDataSetCount = energyDataCount / railInfoCount;
+
+ for (int i = 0; i < energyDataSetCount; 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() + ",";
+ }
+ System.out.println(csvRow);
+ }
+ } else {
+ System.out.println("Error: railInfoCount is zero");
+ }
+ }
+
+ private static void generateCsvFile(String pathToIncidentReport) {
+ try {
+ IncidentReportProto irProto =
+ IncidentReportProto.parseFrom(new FileInputStream(pathToIncidentReport));
+
+ if (irProto.hasIncidentReport()) {
+ PowerStatsServiceProto pssProto = irProto.getIncidentReport();
+ printRailInfo(pssProto);
+ printEnergyData(pssProto);
+ } else {
+ System.out.println("Incident report not found. Exiting.");
+ }
+ } catch (IOException e) {
+ System.out.println("Unable to open incident report file: " + pathToIncidentReport);
+ System.out.println(e);
+ }
+ }
+
+ /**
+ * This is the entry point to parse the ODPM data out of incident reports.
+ * It requires one argument which is the path to the incident_report.proto
+ * file captured in a bugreport.
+ *
+ * @param args Path to incident_report.proto passed in from command line.
+ */
+ public static void main(String[] args) {
+ if (args.length > 0) {
+ generateCsvFile(args[0]);
+ } else {
+ System.err.println("Usage: PowerStatsServiceProtoParser <incident_report.proto>");
+ System.err.println("Missing path to incident_report.proto. Exiting.");
+ System.exit(1);
+ }
+ }
+}
diff --git a/tools/powerstats/PowerStatsServiceProtoParser_manifest.txt b/tools/powerstats/PowerStatsServiceProtoParser_manifest.txt
new file mode 100644
index 0000000..5df1211
--- /dev/null
+++ b/tools/powerstats/PowerStatsServiceProtoParser_manifest.txt
@@ -0,0 +1 @@
+Main-class: com.android.server.powerstats.PowerStatsServiceProtoParser
diff --git a/wifi/OWNERS b/wifi/OWNERS
index 0601047..c1c70e2 100644
--- a/wifi/OWNERS
+++ b/wifi/OWNERS
@@ -1,6 +1,6 @@
set noparent
+dysu@google.com
etancohen@google.com
-mplass@google.com
rpius@google.com
satk@google.com
diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt
index c3e573c..ed5ed82 100644
--- a/wifi/api/system-current.txt
+++ b/wifi/api/system-current.txt
@@ -292,6 +292,7 @@
method public int getBandwidth();
method @Nullable public android.net.MacAddress getBssid();
method public int getFrequency();
+ method public int getWifiStandard();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int CHANNEL_WIDTH_160MHZ = 6; // 0x6
field public static final int CHANNEL_WIDTH_20MHZ = 2; // 0x2
diff --git a/wifi/java/android/net/wifi/SoftApInfo.java b/wifi/java/android/net/wifi/SoftApInfo.java
index 40981f7..cf61f81 100644
--- a/wifi/java/android/net/wifi/SoftApInfo.java
+++ b/wifi/java/android/net/wifi/SoftApInfo.java
@@ -86,8 +86,6 @@
*/
public static final int CHANNEL_WIDTH_160MHZ = 6;
-
-
/** The frequency which AP resides on. */
private int mFrequency = 0;
@@ -99,6 +97,11 @@
private MacAddress mBssid;
/**
+ * The operational mode of the AP.
+ */
+ private @WifiAnnotations.WifiStandard int mWifiStandard = ScanResult.WIFI_STANDARD_UNKNOWN;
+
+ /**
* Get the frequency which AP resides on.
*/
public int getFrequency() {
@@ -163,6 +166,27 @@
}
/**
+ * Set the operational mode of the AP.
+ *
+ * @param wifiStandard values from {@link ScanResult}'s {@code WIFI_STANDARD_}
+ * @hide
+ */
+ public void setWifiStandard(@WifiAnnotations.WifiStandard int wifiStandard) {
+ mWifiStandard = wifiStandard;
+ }
+
+ /**
+ * Get the operational mode of the AP.
+ * @return valid values from {@link ScanResult}'s {@code WIFI_STANDARD_}
+ */
+ public @WifiAnnotations.WifiStandard int getWifiStandard() {
+ if (!SdkLevelUtil.isAtLeastS()) {
+ throw new UnsupportedOperationException();
+ }
+ return mWifiStandard;
+ }
+
+ /**
* @hide
*/
public SoftApInfo(@Nullable SoftApInfo source) {
@@ -170,6 +194,7 @@
mFrequency = source.mFrequency;
mBandwidth = source.mBandwidth;
mBssid = source.mBssid;
+ mWifiStandard = source.mWifiStandard;
}
}
@@ -191,6 +216,7 @@
dest.writeInt(mFrequency);
dest.writeInt(mBandwidth);
dest.writeParcelable(mBssid, flags);
+ dest.writeInt(mWifiStandard);
}
@NonNull
@@ -201,6 +227,7 @@
info.mFrequency = in.readInt();
info.mBandwidth = in.readInt();
info.mBssid = in.readParcelable(MacAddress.class.getClassLoader());
+ info.mWifiStandard = in.readInt();
return info;
}
@@ -215,8 +242,9 @@
StringBuilder sbuf = new StringBuilder();
sbuf.append("SoftApInfo{");
sbuf.append("bandwidth= ").append(mBandwidth);
- sbuf.append(",frequency= ").append(mFrequency);
+ sbuf.append(", frequency= ").append(mFrequency);
if (mBssid != null) sbuf.append(",bssid=").append(mBssid.toString());
+ sbuf.append(", wifiStandard= ").append(mWifiStandard);
sbuf.append("}");
return sbuf.toString();
}
@@ -228,11 +256,12 @@
SoftApInfo softApInfo = (SoftApInfo) o;
return mFrequency == softApInfo.mFrequency
&& mBandwidth == softApInfo.mBandwidth
- && Objects.equals(mBssid, softApInfo.mBssid);
+ && Objects.equals(mBssid, softApInfo.mBssid)
+ && mWifiStandard == softApInfo.mWifiStandard;
}
@Override
public int hashCode() {
- return Objects.hash(mFrequency, mBandwidth, mBssid);
+ return Objects.hash(mFrequency, mBandwidth, mBssid, mWifiStandard);
}
}
diff --git a/wifi/tests/src/android/net/wifi/SoftApInfoTest.java b/wifi/tests/src/android/net/wifi/SoftApInfoTest.java
index 458a954..2821c35 100644
--- a/wifi/tests/src/android/net/wifi/SoftApInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApInfoTest.java
@@ -17,6 +17,7 @@
package android.net.wifi;
import android.net.MacAddress;
+import android.net.wifi.util.SdkLevelUtil;
import android.os.Parcel;
import static org.junit.Assert.assertEquals;
@@ -40,6 +41,7 @@
info.setFrequency(2412);
info.setBandwidth(SoftApInfo.CHANNEL_WIDTH_20MHZ);
info.setBssid(MacAddress.fromString("aa:bb:cc:dd:ee:ff"));
+ info.setWifiStandard(ScanResult.WIFI_STANDARD_LEGACY);
SoftApInfo copiedInfo = new SoftApInfo(info);
@@ -57,6 +59,7 @@
info.setFrequency(2412);
info.setBandwidth(SoftApInfo.CHANNEL_WIDTH_20MHZ);
info.setBssid(MacAddress.fromString("aa:bb:cc:dd:ee:ff"));
+ info.setWifiStandard(ScanResult.WIFI_STANDARD_LEGACY);
Parcel parcelW = Parcel.obtain();
info.writeToParcel(parcelW, 0);
@@ -72,4 +75,19 @@
assertEquals(info.hashCode(), fromParcel.hashCode());
}
+
+ /**
+ * Verifies the initial value same as expected.
+ */
+ @Test
+ public void testInitialValue() throws Exception {
+ SoftApInfo info = new SoftApInfo();
+ assertEquals(info.getFrequency(), 0);
+ assertEquals(info.getBandwidth(), SoftApInfo.CHANNEL_WIDTH_INVALID);
+ if (SdkLevelUtil.isAtLeastS()) {
+ assertEquals(info.getBssid(), null);
+ assertEquals(info.getWifiStandard(), ScanResult.WIFI_STANDARD_UNKNOWN);
+ }
+ }
+
}