Merge "free weakref to linearblock when it is destroy"
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/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/api/current.txt b/api/current.txt
index 922e89c..2b662eb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -27325,6 +27325,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);
@@ -46760,6 +46766,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";
@@ -46987,6 +46994,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/system-current.txt b/api/system-current.txt
index b8fa299..65fe85b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1592,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 {
@@ -4363,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
}
@@ -4378,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
@@ -7205,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
@@ -9374,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";
@@ -10931,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);
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 d57269e..7a7cdd1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1817,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
}
@@ -1832,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
@@ -4139,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
}
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/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index fc1455b..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.
@@ -11433,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/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/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/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 6673cb9..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;
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/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/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/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/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/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/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/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/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/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/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 68e2891..c9a4ab0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -238,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" />
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index fbbdaa3..ed15970 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -239,7 +239,9 @@
<!-- Old synonym for "privileged". Deprecated in API level 23. -->
<flag name="system" value="0x10" />
<!-- Additional flag from base permission type: this permission can also
- (optionally) be granted to development applications. -->
+ (optionally) be granted to development applications. Although undocumented, the
+ permission state used to be shared by all users (including future users), but it is
+ managed per-user since API level 31. -->
<flag name="development" value="0x20" />
<!-- Additional flag from base permission type: this permission is closely
associated with an app op for controlling access. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ee6267a..592afa7 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3267,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
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a5e5fba..03975da 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -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 fdcd39a..8cd4b48 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" />
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/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/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/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/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/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/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/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/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/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/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/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 8a2976f..480e494 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -27138,6 +27138,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);
@@ -44903,6 +44909,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";
@@ -45130,6 +45137,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/system-current.txt b/non-updatable-api/system-current.txt
index 38f745e..82f2021 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -1532,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 {
@@ -4303,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
}
@@ -4318,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
@@ -8238,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";
@@ -9795,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);
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/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/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/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/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/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/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 06c190f..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,10 +37,16 @@
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;
@@ -60,8 +65,8 @@
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.
@@ -88,7 +93,7 @@
// interrupt is being tracked and a timeout is used as a last resort to turn off high brightness
// mode.
private boolean mIsAodInterruptActive;
- private final Runnable mAodInterruptTimeoutAction = this::onCancelAodInterrupt;
+ @Nullable private Runnable mCancelAodTimeoutAction;
public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
@Override
@@ -138,18 +143,27 @@
@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);
@@ -157,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];
@@ -170,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]));
@@ -194,7 +208,7 @@
}
private void showUdfpsOverlay() {
- mHandler.post(() -> {
+ mFgExecutor.execute(() -> {
if (!mIsOverlayShowing) {
try {
Log.v(TAG, "showUdfpsOverlay | adding window");
@@ -211,7 +225,7 @@
}
private void hideUdfpsOverlay() {
- mHandler.post(() -> {
+ mFgExecutor.execute(() -> {
if (mIsOverlayShowing) {
Log.v(TAG, "hideUdfpsOverlay | removing window");
mView.setOnTouchListener(null);
@@ -228,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);
@@ -265,7 +279,8 @@
// 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.
- mHandler.postDelayed(mAodInterruptTimeoutAction, AOD_INTERRUPT_TIMEOUT_MILLIS);
+ 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);
}
@@ -280,7 +295,10 @@
if (!mIsAodInterruptActive) {
return;
}
- mHandler.removeCallbacks(mAodInterruptTimeoutAction);
+ if (mCancelAodTimeoutAction != null) {
+ mCancelAodTimeoutAction.run();
+ mCancelAodTimeoutAction = null;
+ }
mIsAodInterruptActive = false;
onFingerUp();
}
@@ -338,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/BubbleLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
index 2d90c86..ea612af 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
@@ -33,7 +33,7 @@
* @param e UI event
*/
public void log(Bubble b, UiEventEnum e) {
- super.log(e, b.getUser().getIdentifier(), b.getPackageName());
+ logWithInstanceId(e, b.getAppUid(), b.getPackageName(), b.getInstanceId());
}
/**
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 b35579d..79925ba 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -62,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;
@@ -183,6 +184,12 @@
@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/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 b872b1a..5aeb8df 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -73,7 +73,6 @@
* --ei brightness_bucket 1}
*/
private int mDebugBrightnessBucket = -1;
- private DozeMachine.State mState;
@Inject
public DozeScreenBrightness(Context context, @WrappedService DozeMachine.Service service,
@@ -94,7 +93,6 @@
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
- mState = newState;
switch (newState) {
case INITIALIZED:
case DOZE:
@@ -112,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);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 781bf8d..f8f4f4d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -43,7 +43,7 @@
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.settingslib.R;
+import com.android.systemui.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
/**
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/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/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/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/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/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 7a12464..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
@@ -106,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;
@@ -449,12 +448,11 @@
private DismissListener mDismissListener;
private DismissAllAnimationListener mDismissAllAnimationListener;
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;
@@ -553,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);
@@ -580,9 +571,7 @@
if (mFgsSectionView != null) {
return;
}
-
mFgsSectionView = fgsSectionView;
-
addView(mFgsSectionView, -1);
}
@@ -603,7 +592,6 @@
inflateEmptyShadeView();
inflateFooterView();
- mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation);
}
/**
@@ -1021,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);
@@ -5371,9 +5342,8 @@
}
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;
}
@@ -5409,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);
@@ -5697,6 +5667,10 @@
mRemoteInputManager = remoteInputManager;
}
+ void setShadeController(ShadeController shadeController) {
+ mShadeController = shadeController;
+ }
+
/**
* A listener that is notified when the empty space below the notifications is clicked on
*/
@@ -6096,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 */);
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 231ff2c..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;
@@ -85,6 +85,7 @@
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;
@@ -104,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;
@@ -139,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;
@@ -153,6 +154,8 @@
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;
@@ -389,8 +392,6 @@
return;
}
- boolean isBlockingHelperShown = false;
-
mView.removeDraggedView(view);
mView.updateContinuousShadowDrawing();
@@ -400,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(
@@ -559,7 +557,6 @@
NotificationLockscreenUserManager lockscreenUserManager,
MetricsLogger metricsLogger,
FalsingManager falsingManager,
- NotificationSectionsManager notificationSectionsManager,
@Main Resources resources,
NotificationSwipeHelper.Builder notificationSwipeHelperBuilder,
StatusBar statusBar,
@@ -576,7 +573,9 @@
ForegroundServiceDismissalFeatureController fgFeatureController,
ForegroundServiceSectionController fgServicesSectionController,
LayoutInflater layoutInflater,
- NotificationRemoteInputManager remoteInputManager) {
+ NotificationRemoteInputManager remoteInputManager,
+ VisualStabilityManager visualStabilityManager,
+ ShadeController shadeController) {
mAllowLongPress = allowLongPress;
mNotificationGutsManager = notificationGutsManager;
mHeadsUpManager = headsUpManager;
@@ -592,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) {
@@ -622,6 +619,8 @@
mFgServicesSectionController = fgServicesSectionController;
mLayoutInflater = layoutInflater;
mRemoteInputManager = remoteInputManager;
+ mVisualStabilityManager = visualStabilityManager;
+ mShadeController = shadeController;
}
public void attach(NotificationStackScrollLayout view) {
@@ -635,6 +634,7 @@
mView.setFooterDismissListener(() ->
mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES));
mView.setRemoteInputManager(mRemoteInputManager);
+ mView.setShadeController(mShadeController);
if (mFgFeatureController.isForegroundServiceDismissalEnabled()) {
mView.initializeForegroundServiceSection(
@@ -681,6 +681,8 @@
mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate);
mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded);
+ mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation);
+
mTunerService.addTunable(
(key, newValue) -> {
switch (key) {
@@ -723,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);
}
@@ -1459,7 +1477,7 @@
@Override
public boolean isInVisibleLocation(NotificationEntry entry) {
- return mView.isInVisibleLocation(entry);
+ return NotificationStackScrollLayoutController.this.isInVisibleLocation(entry);
}
@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/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/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/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/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index b85b1c2..be0865d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -115,8 +115,6 @@
@Test
public void testAod_usesLightSensor() {
- mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
- mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.onScreenState(Display.STATE_DOZE);
waitForSensorManager();
@@ -127,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();
@@ -181,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);
@@ -197,6 +192,18 @@
}
@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,
Optional.empty() /* sensor */, mDozeHost, null /* handler */,
@@ -206,12 +213,15 @@
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/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/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 0d283ce..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.
@@ -31,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;
@@ -56,15 +58,14 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
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;
@@ -88,7 +89,6 @@
@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;
@@ -117,10 +117,6 @@
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.injectMockDependency(ShadeController.class);
@@ -217,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
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 d834176..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
@@ -60,6 +60,7 @@
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;
@@ -68,6 +69,7 @@
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;
@@ -105,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;
@@ -126,6 +127,8 @@
@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;
@@ -158,7 +161,6 @@
mNotificationLockscreenUserManager,
mMetricsLogger,
mFalsingManager,
- mNotificationSectionsManager,
mResources,
mNotificationSwipeHelperBuilder,
mStatusBar,
@@ -175,7 +177,9 @@
mFgFeatureController,
mFgServicesSectionController,
mLayoutInflater,
- mRemoteInputManager
+ mRemoteInputManager,
+ mVisualStabilityManager,
+ mShadeController
);
when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true);
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/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/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index c9e5d01..6ca99d1 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -83,6 +83,7 @@
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;
@@ -484,6 +485,10 @@
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 -> {
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/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 2522e93..ba2e147 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2145,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/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/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/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 8468f23..48efa5c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -745,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;
}
@@ -2357,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);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e1363f9..3ca12ae 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1850,7 +1850,7 @@
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mLock) {
removeMessages(WRITE_PACKAGE_LIST);
- mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
+ mPermissionManager.writeStateToPackageSettingsTEMP();
mSettings.writePackageListLPr(msg.arg1);
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
@@ -3095,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
@@ -3521,7 +3524,7 @@
+ ((SystemClock.uptimeMillis()-startTime)/1000f)
+ " seconds");
- mPermissionManager.readPermissionsStateFromPackageSettingsTEMP();
+ mPermissionManager.readStateFromPackageSettingsTEMP();
// If the platform SDK has changed since the last time we booted,
// we need to re-grant app permission to catch any new ones that
// appear. This is really a hack, and means that apps can in some
@@ -21835,7 +21838,7 @@
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
- mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
+ mPermissionManager.writeStateToPackageSettingsTEMP();
DumpState dumpState = new DumpState();
boolean fullPreferred = false;
@@ -23715,7 +23718,7 @@
mDirtyUsers.remove(userId);
mUserNeedsBadging.delete(userId);
mPermissionManager.onUserRemoved(userId);
- mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
+ mPermissionManager.writeStateToPackageSettingsTEMP();
mSettings.removeUserLPw(userId);
mPendingBroadcasts.remove(userId);
mInstantAppRegistry.onUserRemovedLPw(userId);
@@ -23816,9 +23819,9 @@
boolean readPermissionStateForUser(@UserIdInt int userId) {
synchronized (mPackages) {
- mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
+ mPermissionManager.writeStateToPackageSettingsTEMP();
mSettings.readPermissionStateForUserSyncLPr(userId);
- mPermissionManager.readPermissionsStateFromPackageSettingsTEMP();
+ mPermissionManager.readStateFromPackageSettingsTEMP();
return mPmInternal.isPermissionUpgradeNeeded(userId);
}
}
@@ -25882,12 +25885,12 @@
/**
* Temporary method that wraps mSettings.writeLPr() and calls
- * mPermissionManager.writePermissionsStateToPackageSettingsTEMP() beforehand.
+ * mPermissionManager.writeStateToPackageSettingsTEMP() beforehand.
*
* TODO(zhanghai): This should be removed once we finish migration of permission storage.
*/
private void writeSettingsLPrTEMP() {
- mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
+ mPermissionManager.writeStateToPackageSettingsTEMP();
mSettings.writeLPr();
}
}
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/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index b12a8f8..d413213 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -164,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();
}
@@ -865,7 +854,6 @@
}
void commitSession(@NonNull PackageInstallerSession session) {
- updateStoredSession(session);
mPreRebootVerificationHandler.startPreRebootVerification(session);
}
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/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 962638b..865b8a1 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -36,6 +36,7 @@
import android.util.Log;
import android.util.Slog;
+import com.android.internal.util.ArrayUtils;
import com.android.server.pm.DumpState;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.PackageSettingBase;
@@ -139,6 +140,10 @@
this.perm = perm;
}
+ public boolean hasGids() {
+ return !ArrayUtils.isEmpty(gids);
+ }
+
public int[] computeGids(int userId) {
if (perUser) {
final int[] userGids = new int[gids.length];
@@ -419,9 +424,9 @@
}
public void enforceDeclaredUsedAndRuntimeOrDevelopment(AndroidPackage pkg,
- PermissionsState permsState) {
+ UidPermissionState uidState) {
int index = pkg.getRequestedPermissions().indexOf(name);
- if (!permsState.hasRequestedPermission(name) && index == -1) {
+ if (!uidState.hasRequestedPermission(name) && index == -1) {
throw new SecurityException("Package " + pkg.getPackageName()
+ " has not requested permission " + name);
}
diff --git a/services/core/java/com/android/server/pm/permission/DevicePermissionState.java b/services/core/java/com/android/server/pm/permission/DevicePermissionState.java
new file mode 100644
index 0000000..b9456acf
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/DevicePermissionState.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.server.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * Permission state for this device.
+ */
+public final class DevicePermissionState {
+ @GuardedBy("mLock")
+ @NonNull
+ private final SparseArray<UserPermissionState> mUserStates = new SparseArray<>();
+
+ @NonNull
+ private final Object mLock;
+
+ public DevicePermissionState(@NonNull Object lock) {
+ mLock = lock;
+ }
+
+ @Nullable
+ public UserPermissionState getUserState(@UserIdInt int userId) {
+ synchronized (mLock) {
+ return mUserStates.get(userId);
+ }
+ }
+
+ @NonNull
+ public UserPermissionState getOrCreateUserState(@UserIdInt int userId) {
+ synchronized (mLock) {
+ UserPermissionState userState = mUserStates.get(userId);
+ if (userState == null) {
+ userState = new UserPermissionState(mLock);
+ mUserStates.put(userId, userState);
+ }
+ return userState;
+ }
+ }
+
+ public void removeUserState(@UserIdInt int userId) {
+ synchronized (mLock) {
+ mUserStates.delete(userId);
+ }
+ }
+
+ public int[] getUserIds() {
+ synchronized (mLock) {
+ final int userStatesSize = mUserStates.size();
+ final int[] userIds = new int[userStatesSize];
+ for (int i = 0; i < userStatesSize; i++) {
+ final int userId = mUserStates.keyAt(i);
+ userIds[i] = userId;
+ }
+ return userIds;
+ }
+ }
+}
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 1cfc5b1..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;
@@ -148,12 +148,9 @@
import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultDialerProvider;
import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultHomeProvider;
import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback;
-import com.android.server.pm.permission.PermissionsState.PermissionState;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.SoftRestrictedPermissionPolicy;
-import libcore.util.EmptyArray;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -226,8 +223,8 @@
/** Internal connection to the user manager */
private final UserManagerInternal mUserManagerInt;
- /** Maps from App ID to PermissionsState */
- private final SparseArray<PermissionsState> mAppIdStates = new SparseArray<>();
+ @NonNull
+ private final DevicePermissionState mState;
/** Permission controller: User space permission management */
private PermissionControllerManager mPermissionControllerManager;
@@ -395,6 +392,7 @@
mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
mSettings = new PermissionSettings(mLock);
+ mState = new DevicePermissionState(mLock);
mAppOpsManager = context.getSystemService(AppOpsManager.class);
mHandlerThread = new ServiceThread(TAG,
@@ -681,12 +679,12 @@
if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
return 0;
}
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + packageName);
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
return 0;
}
- return permissionsState.getPermissionFlags(permName, userId);
+ return uidState.getPermissionFlags(permName);
}
@Override
@@ -788,14 +786,13 @@
throw new IllegalArgumentException("Unknown permission: " + permName);
}
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + packageName);
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
return;
}
- final boolean hadState =
- permissionsState.getRuntimePermissionState(permName, userId) != null;
+ final boolean hadState = uidState.getPermissionState(permName) != null;
if (!hadState) {
boolean isRequested = false;
// Fast path, the current package has requested the permission.
@@ -822,20 +819,18 @@
}
}
final boolean permissionUpdated =
- permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues);
+ uidState.updatePermissionFlags(bp, flagMask, flagValues);
if (permissionUpdated && bp.isRuntime()) {
notifyRuntimePermissionStateChanged(packageName, userId);
}
if (permissionUpdated && callback != null) {
// Install and runtime permissions are stored in different places,
// so figure out what permission changed and persist the change.
- if (permissionsState.getInstallPermissionState(permName) != null) {
+ if (!bp.isRuntime()) {
int userUid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
callback.onInstallPermissionUpdatedNotifyListener(userUid);
- } else if (permissionsState.getRuntimePermissionState(permName, userId) != null
- || hadState) {
- callback.onPermissionUpdatedNotifyListener(new int[]{userId}, false,
- pkg.getUid());
+ } else {
+ callback.onPermissionUpdatedNotifyListener(new int[]{userId}, false, pkg.getUid());
}
}
}
@@ -868,13 +863,14 @@
final boolean[] changed = new boolean[1];
mPackageManagerInt.forEachPackage(pkg -> {
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
return;
}
- changed[0] |= permissionsState.updatePermissionFlagsForAllPermissions(
- userId, effectiveFlagMask, effectiveFlagValues);
+ changed[0] |= uidState.updatePermissionFlagsForAllPermissions(
+ effectiveFlagMask, effectiveFlagValues);
mOnPermissionChangeListeners.onPermissionsChanged(pkg.getUid());
});
@@ -926,19 +922,20 @@
}
final int uid = UserHandle.getUid(userId, pkg.getUid());
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
return PackageManager.PERMISSION_DENIED;
}
- if (checkSinglePermissionInternal(uid, permissionsState, permissionName)) {
+ if (checkSinglePermissionInternal(uid, uidState, permissionName)) {
return PackageManager.PERMISSION_GRANTED;
}
final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
if (fullerPermissionName != null
- && checkSinglePermissionInternal(uid, permissionsState, fullerPermissionName)) {
+ && checkSinglePermissionInternal(uid, uidState, fullerPermissionName)) {
return PackageManager.PERMISSION_GRANTED;
}
@@ -946,8 +943,8 @@
}
private boolean checkSinglePermissionInternal(int uid,
- @NonNull PermissionsState permissionsState, @NonNull String permissionName) {
- if (!permissionsState.hasPermission(permissionName, UserHandle.getUserId(uid))) {
+ @NonNull UidPermissionState uidState, @NonNull String permissionName) {
+ if (!uidState.hasPermission(permissionName)) {
return false;
}
@@ -1141,9 +1138,9 @@
final long identity = Binder.clearCallingIdentity();
try {
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + packageName);
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
return null;
}
@@ -1164,7 +1161,7 @@
for (int i = 0; i < permissionCount; i++) {
final String permissionName = pkg.getRequestedPermissions().get(i);
final int currentFlags =
- permissionsState.getPermissionFlags(permissionName, userId);
+ uidState.getPermissionFlags(permissionName);
if ((currentFlags & queryFlags) != 0) {
if (whitelistedPermissions == null) {
whitelistedPermissions = new ArrayList<>();
@@ -1453,13 +1450,14 @@
throw new IllegalArgumentException("Unknown package: " + packageName);
}
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
return;
}
- bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, permissionsState);
+ bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, uidState);
// If a permission review is required for legacy apps we represent
// their permissions as always granted runtime ones since we need
@@ -1472,7 +1470,7 @@
final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
- final int flags = permissionsState.getPermissionFlags(permName, userId);
+ final int flags = uidState.getPermissionFlags(permName);
if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
Log.e(TAG, "Cannot grant system fixed permission "
+ permName + " for package " + packageName);
@@ -1502,8 +1500,9 @@
if (bp.isDevelopment()) {
// Development permissions must be handled specially, since they are not
// normal runtime permissions. For now they apply to all users.
- if (permissionsState.grantInstallPermission(bp)
- != PERMISSION_OPERATION_FAILURE) {
+ // TODO(zhanghai): We are breaking the behavior above by making all permission state
+ // per-user. It isn't documented behavior and relatively rarely used anyway.
+ if (uidState.grantPermission(bp) != PERMISSION_OPERATION_FAILURE) {
if (callback != null) {
callback.onInstallPermissionGranted();
}
@@ -1521,13 +1520,13 @@
return;
}
- final int result = permissionsState.grantRuntimePermission(bp, userId);
+ final int result = uidState.grantPermission(bp);
switch (result) {
case PERMISSION_OPERATION_FAILURE: {
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);
}
@@ -1617,13 +1616,14 @@
throw new IllegalArgumentException("Unknown permission: " + permName);
}
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
return;
}
- bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, permissionsState);
+ bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, uidState);
// If a permission review is required for legacy apps we represent
// their permissions as always granted runtime ones since we need
@@ -1634,7 +1634,7 @@
return;
}
- final int flags = permissionsState.getPermissionFlags(permName, userId);
+ final int flags = uidState.getPermissionFlags(permName);
// Only the system may revoke SYSTEM_FIXED permissions.
if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
&& UserHandle.getCallingAppId() != Process.SYSTEM_UID) {
@@ -1649,8 +1649,9 @@
if (bp.isDevelopment()) {
// Development permissions must be handled specially, since they are not
// normal runtime permissions. For now they apply to all users.
- if (permissionsState.revokeInstallPermission(bp)
- != PERMISSION_OPERATION_FAILURE) {
+ // TODO(zhanghai): We are breaking the behavior above by making all permission state
+ // per-user. It isn't documented behavior and relatively rarely used anyway.
+ if (uidState.revokePermission(bp) != PERMISSION_OPERATION_FAILURE) {
if (callback != null) {
mDefaultPermissionCallback.onInstallPermissionRevoked();
}
@@ -1659,12 +1660,11 @@
}
// Permission is already revoked, no need to do anything.
- if (!permissionsState.hasRuntimePermission(permName, userId)) {
+ if (!uidState.hasPermission(permName)) {
return;
}
- if (permissionsState.revokeRuntimePermission(bp, userId)
- == PERMISSION_OPERATION_FAILURE) {
+ if (uidState.revokePermission(bp) == PERMISSION_OPERATION_FAILURE) {
return;
}
@@ -2466,19 +2466,7 @@
private void onUserRemoved(@UserIdInt int userId) {
synchronized (mLock) {
- final int appIdStatesSize = mAppIdStates.size();
- for (int i = 0; i < appIdStatesSize; i++) {
- PermissionsState permissionsState = mAppIdStates.valueAt(i);
- for (PermissionState permissionState
- : permissionsState.getRuntimePermissionStates(userId)) {
- BasePermission bp = mSettings.getPermission(permissionState.getName());
- if (bp != null) {
- permissionsState.revokeRuntimePermission(bp, userId);
- permissionsState.updatePermissionFlags(bp, userId,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
- }
- }
- }
+ mState.removeUserState(userId);
}
}
@@ -2489,19 +2477,18 @@
if (ps == null) {
return Collections.emptySet();
}
- final PermissionsState permissionsState = getPermissionsState(ps);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + packageName);
+ final UidPermissionState uidState = getUidState(ps, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
return Collections.emptySet();
}
if (!ps.getInstantApp(userId)) {
- return permissionsState.getPermissions(userId);
+ return uidState.getPermissions();
} else {
// Install permission state is shared among all users, but instant app state is
// per-user, so we can only filter it here unless we make install permission state
// per-user as well.
- final Set<String> instantPermissions = new ArraySet<>(permissionsState.getPermissions(
- userId));
+ final Set<String> instantPermissions = new ArraySet<>(uidState.getPermissions());
instantPermissions.removeIf(permissionName -> {
BasePermission permission = mSettings.getPermission(permissionName);
if (permission == null) {
@@ -2533,12 +2520,12 @@
if (ps == null) {
return null;
}
- final PermissionsState permissionsState = getPermissionsState(ps);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + packageName);
+ final UidPermissionState uidState = getUidState(ps, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
return null;
}
- return permissionsState.computeGids(userId);
+ return uidState.computeGids(userId);
}
/**
@@ -2575,15 +2562,17 @@
if (ps == null) {
return;
}
- final PermissionsState permissionsState = getOrCreatePermissionsState(ps);
final int[] userIds = getAllUserIds();
boolean runtimePermissionsRevoked = false;
int[] updatedUserIds = EMPTY_INT_ARRAY;
- for (int userId : userIds) {
- if (permissionsState.isMissing(userId)) {
+ for (final int userId : userIds) {
+ final UserPermissionState userState = mState.getOrCreateUserState(userId);
+ final UidPermissionState uidState = userState.getOrCreateUidState(ps.getAppId());
+
+ if (uidState.isMissing()) {
Collection<String> requestedPermissions;
int targetSdkVersion;
if (!ps.isSharedUser()) {
@@ -2611,222 +2600,221 @@
&& permission.isRuntime() && !permission.isRemoved()) {
if (permission.isHardOrSoftRestricted()
|| permission.isImmutablyRestricted()) {
- permissionsState.updatePermissionFlags(permission, userId,
+ uidState.updatePermissionFlags(permission,
FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT);
}
if (targetSdkVersion < Build.VERSION_CODES.M) {
- permissionsState.updatePermissionFlags(permission, userId,
+ uidState.updatePermissionFlags(permission,
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
| PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
| PackageManager.FLAG_PERMISSION_REVOKED_COMPAT);
- permissionsState.grantRuntimePermission(permission, userId);
+ uidState.grantPermission(permission);
}
}
}
- permissionsState.setMissing(false, userId);
+ uidState.setMissing(false);
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
}
- }
- PermissionsState origPermissions = permissionsState;
+ UidPermissionState origState = uidState;
- boolean changedInstallPermission = false;
+ boolean changedInstallPermission = false;
- if (replace) {
- ps.setInstallPermissionsFixed(false);
- if (!ps.isSharedUser()) {
- origPermissions = new PermissionsState(permissionsState);
- permissionsState.reset();
- } else {
- // We need to know only about runtime permission changes since the
- // calling code always writes the install permissions state but
- // the runtime ones are written only if changed. The only cases of
- // changed runtime permissions here are promotion of an install to
- // runtime and revocation of a runtime from a shared user.
- synchronized (mLock) {
- updatedUserIds = revokeUnusedSharedUserPermissionsLocked(
- ps.getSharedUser().getPackages(), permissionsState, userIds);
- if (!ArrayUtils.isEmpty(updatedUserIds)) {
- runtimePermissionsRevoked = true;
- }
- }
- }
- }
-
- permissionsState.setGlobalGids(mGlobalGids);
-
- ArraySet<String> newImplicitPermissions = new ArraySet<>();
-
- final int N = pkg.getRequestedPermissions().size();
- for (int i = 0; i < N; i++) {
- final String permName = pkg.getRequestedPermissions().get(i);
- final BasePermission bp = mSettings.getPermission(permName);
- final boolean appSupportsRuntimePermissions =
- pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M;
- String upgradedActivityRecognitionPermission = null;
-
- if (DEBUG_INSTALL && bp != null) {
- Log.i(TAG, "Package " + pkg.getPackageName()
- + " checking " + permName + ": " + bp);
- }
-
- if (bp == null || getSourcePackageSetting(bp) == null) {
- if (packageOfInterest == null || packageOfInterest.equals(
- pkg.getPackageName())) {
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, "Unknown permission " + permName
- + " in package " + pkg.getPackageName());
- }
- }
- continue;
- }
-
- // Cache newImplicitPermissions before modifing permissionsState as for the shared
- // uids the original and new state are the same object
- if (!origPermissions.hasRequestedPermission(permName)
- && (pkg.getImplicitPermissions().contains(permName)
- || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
- if (pkg.getImplicitPermissions().contains(permName)) {
- // If permName is an implicit permission, try to auto-grant
- newImplicitPermissions.add(permName);
-
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, permName + " is newly added for " + pkg.getPackageName());
- }
+ if (replace) {
+ userState.setInstallPermissionsFixed(ps.name, false);
+ if (!ps.isSharedUser()) {
+ origState = new UidPermissionState(uidState);
+ uidState.reset();
} else {
- // Special case for Activity Recognition permission. Even if AR permission
- // is not an implicit permission we want to add it to the list (try to
- // auto-grant it) if the app was installed on a device before AR permission
- // was split, regardless of if the app now requests the new AR permission
- // or has updated its target SDK and AR is no longer implicit to it.
- // This is a compatibility workaround for apps when AR permission was
- // split in Q.
- final List<SplitPermissionInfoParcelable> permissionList =
- getSplitPermissions();
- int numSplitPerms = permissionList.size();
- for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
- SplitPermissionInfoParcelable sp = permissionList.get(splitPermNum);
- String splitPermName = sp.getSplitPermission();
- if (sp.getNewPermissions().contains(permName)
- && origPermissions.hasInstallPermission(splitPermName)) {
- upgradedActivityRecognitionPermission = splitPermName;
- newImplicitPermissions.add(permName);
+ // We need to know only about runtime permission changes since the
+ // calling code always writes the install permissions state but
+ // the runtime ones are written only if changed. The only cases of
+ // changed runtime permissions here are promotion of an install to
+ // runtime and revocation of a runtime from a shared user.
+ synchronized (mLock) {
+ if (revokeUnusedSharedUserPermissionsLocked(
+ ps.getSharedUser().getPackages(), uidState)) {
+ updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+ runtimePermissionsRevoked = true;
+ }
+ }
+ }
+ }
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, permName + " is newly added for "
- + pkg.getPackageName());
+ 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++) {
+ final String permName = pkg.getRequestedPermissions().get(i);
+ final BasePermission bp = mSettings.getPermission(permName);
+ final boolean appSupportsRuntimePermissions =
+ pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M;
+ String upgradedActivityRecognitionPermission = null;
+
+ if (DEBUG_INSTALL && bp != null) {
+ Log.i(TAG, "Package " + friendlyName
+ + " checking " + permName + ": " + bp);
+ }
+
+ if (bp == null || getSourcePackageSetting(bp) == null) {
+ if (packageOfInterest == null || packageOfInterest.equals(
+ pkg.getPackageName())) {
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Unknown permission " + permName
+ + " in package " + friendlyName);
+ }
+ }
+ continue;
+ }
+
+ // Cache newImplicitPermissions before modifing permissionsState as for the shared
+ // uids the original and new state are the same object
+ if (!origState.hasRequestedPermission(permName)
+ && (pkg.getImplicitPermissions().contains(permName)
+ || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
+ if (pkg.getImplicitPermissions().contains(permName)) {
+ // If permName is an implicit permission, try to auto-grant
+ newImplicitPermissions.add(permName);
+
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, permName + " is newly added for " + friendlyName);
+ }
+ } else {
+ // Special case for Activity Recognition permission. Even if AR permission
+ // is not an implicit permission we want to add it to the list (try to
+ // auto-grant it) if the app was installed on a device before AR permission
+ // was split, regardless of if the app now requests the new AR permission
+ // or has updated its target SDK and AR is no longer implicit to it.
+ // This is a compatibility workaround for apps when AR permission was
+ // split in Q.
+ final List<SplitPermissionInfoParcelable> permissionList =
+ getSplitPermissions();
+ int numSplitPerms = permissionList.size();
+ for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
+ SplitPermissionInfoParcelable sp = permissionList.get(splitPermNum);
+ String splitPermName = sp.getSplitPermission();
+ if (sp.getNewPermissions().contains(permName)
+ && origState.hasInstallPermission(splitPermName)) {
+ upgradedActivityRecognitionPermission = splitPermName;
+ newImplicitPermissions.add(permName);
+
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, permName + " is newly added for "
+ + friendlyName);
+ }
+ break;
}
- break;
}
}
}
- }
- // TODO(b/140256621): The package instant app method has been removed
- // as part of work in b/135203078, so this has been commented out in the meantime
- // Limit ephemeral apps to ephemeral allowed permissions.
-// if (/*pkg.isInstantApp()*/ false && !bp.isInstant()) {
-// if (DEBUG_PERMISSIONS) {
-// Log.i(TAG, "Denying non-ephemeral permission " + bp.getName()
-// + " for package " + pkg.getPackageName());
-// }
-// continue;
-// }
+ // TODO(b/140256621): The package instant app method has been removed
+ // as part of work in b/135203078, so this has been commented out in the meantime
+ // Limit ephemeral apps to ephemeral allowed permissions.
+ // if (/*pkg.isInstantApp()*/ false && !bp.isInstant()) {
+ // if (DEBUG_PERMISSIONS) {
+ // Log.i(TAG, "Denying non-ephemeral permission " + bp.getName()
+ // + " for package " + pkg.getPackageName());
+ // }
+ // continue;
+ // }
- if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
- if (DEBUG_PERMISSIONS) {
- Log.i(TAG, "Denying runtime-only permission " + bp.getName()
- + " for package " + pkg.getPackageName());
+ if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
+ if (DEBUG_PERMISSIONS) {
+ Log.i(TAG, "Denying runtime-only permission " + bp.getName()
+ + " for package " + friendlyName);
+ }
+ continue;
}
- continue;
- }
- final String perm = bp.getName();
- boolean allowedSig = false;
- int grant = GRANT_DENIED;
+ final String perm = bp.getName();
+ boolean allowedSig = false;
+ int grant = GRANT_DENIED;
- // Keep track of app op permissions.
- if (bp.isAppOp()) {
- mSettings.addAppOpPackage(perm, pkg.getPackageName());
- }
-
- if (bp.isNormal()) {
- // For all apps normal permissions are install time ones.
- grant = GRANT_INSTALL;
- } else if (bp.isRuntime()) {
- if (origPermissions.hasInstallPermission(bp.getName())
- || upgradedActivityRecognitionPermission != null) {
- // Before Q we represented some runtime permissions as install permissions,
- // in Q we cannot do this anymore. Hence upgrade them all.
- grant = GRANT_UPGRADE;
- } else {
- // For modern apps keep runtime permissions unchanged.
- grant = GRANT_RUNTIME;
+ // Keep track of app op permissions.
+ if (bp.isAppOp()) {
+ mSettings.addAppOpPackage(perm, pkg.getPackageName());
}
- } else if (bp.isSignature()) {
- // For all apps signature permissions are install time ones.
- allowedSig = shouldGrantSignaturePermission(perm, pkg, ps, bp, origPermissions);
- if (allowedSig) {
+
+ if (bp.isNormal()) {
+ // For all apps normal permissions are install time ones.
grant = GRANT_INSTALL;
+ } else if (bp.isRuntime()) {
+ if (origState.hasInstallPermission(bp.getName())
+ || upgradedActivityRecognitionPermission != null) {
+ // Before Q we represented some runtime permissions as install permissions,
+ // in Q we cannot do this anymore. Hence upgrade them all.
+ grant = GRANT_UPGRADE;
+ } else {
+ // For modern apps keep runtime permissions unchanged.
+ grant = GRANT_RUNTIME;
+ }
+ } else if (bp.isSignature()) {
+ // For all apps signature permissions are install time ones.
+ allowedSig = shouldGrantSignaturePermission(perm, pkg, ps, bp, origState);
+ if (allowedSig) {
+ grant = GRANT_INSTALL;
+ }
}
- }
- if (grant != GRANT_DENIED) {
- if (!ps.isSystem() && ps.areInstallPermissionsFixed() && !bp.isRuntime()) {
- // If this is an existing, non-system package, then
- // we can't add any new permissions to it. Runtime
- // permissions can be added any time - they ad dynamic.
- if (!allowedSig && !origPermissions.hasInstallPermission(perm)) {
- // Except... if this is a permission that was added
- // to the platform (note: need to only do this when
- // updating the platform).
- if (!isNewPlatformPermissionForPackage(perm, pkg)) {
- grant = GRANT_DENIED;
+ if (grant != GRANT_DENIED) {
+ if (!ps.isSystem() && userState.areInstallPermissionsFixed(ps.name)
+ && !bp.isRuntime()) {
+ // If this is an existing, non-system package, then
+ // we can't add any new permissions to it. Runtime
+ // permissions can be added any time - they ad dynamic.
+ if (!allowedSig && !origState.hasInstallPermission(perm)) {
+ // Except... if this is a permission that was added
+ // to the platform (note: need to only do this when
+ // updating the platform).
+ if (!isNewPlatformPermissionForPackage(perm, pkg)) {
+ grant = GRANT_DENIED;
+ }
}
}
}
- }
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, "Considering granting permission " + perm + " to package "
- + pkg.getPackageName());
- }
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Considering granting permission " + perm + " to package "
+ + pkg.getPackageName());
+ }
- synchronized (mLock) {
- if (grant != GRANT_DENIED) {
- switch (grant) {
- case GRANT_INSTALL: {
- // Revoke this as runtime permission to handle the case of
- // a runtime permission being downgraded to an install one.
- // Also in permission review mode we keep dangerous permissions
- // for legacy apps
- for (int userId : userIds) {
- if (origPermissions.getRuntimePermissionState(
- perm, userId) != null) {
+ synchronized (mLock) {
+ if (grant != GRANT_DENIED) {
+ switch (grant) {
+ case GRANT_INSTALL: {
+ // Revoke this as runtime permission to handle the case of
+ // a runtime permission being downgraded to an install one.
+ // Also in permission review mode we keep dangerous permissions
+ // for legacy apps
+ final PermissionState origPermissionState =
+ origState.getPermissionState(perm);
+ if (origPermissionState != null
+ && origPermissionState.isRuntime()) {
// Revoke the runtime permission and clear the flags.
- origPermissions.revokeRuntimePermission(bp, userId);
- origPermissions.updatePermissionFlags(bp, userId,
+ origState.revokePermission(bp);
+ origState.updatePermissionFlags(bp,
PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
// If we revoked a permission permission, we have to write.
updatedUserIds = ArrayUtils.appendInt(
updatedUserIds, userId);
}
- }
- // Grant an install permission.
- if (permissionsState.grantInstallPermission(bp) !=
- PERMISSION_OPERATION_FAILURE) {
- changedInstallPermission = true;
- }
- } break;
+ // Grant an install permission.
+ if (uidState.grantPermission(bp) != PERMISSION_OPERATION_FAILURE) {
+ changedInstallPermission = true;
+ }
+ } break;
- case GRANT_RUNTIME: {
- boolean hardRestricted = bp.isHardRestricted();
- boolean softRestricted = bp.isSoftRestricted();
+ case GRANT_RUNTIME: {
+ boolean hardRestricted = bp.isHardRestricted();
+ boolean softRestricted = bp.isSoftRestricted();
- for (int userId : userIds) {
// If permission policy is not ready we don't deal with restricted
// permissions as the policy may whitelist some permissions. Once
// the policy is initialized we would re-evaluate permissions.
@@ -2834,25 +2822,24 @@
mPermissionPolicyInternal != null
&& mPermissionPolicyInternal.isInitialized(userId);
- PermissionState permState = origPermissions
- .getRuntimePermissionState(perm, userId);
- int flags = permState != null ? permState.getFlags() : 0;
+ PermissionState origPermState = origState.getPermissionState(perm);
+ int flags = origPermState != null ? origPermState.getFlags() : 0;
boolean wasChanged = false;
boolean restrictionExempt =
- (origPermissions.getPermissionFlags(bp.name, userId)
+ (origState.getPermissionFlags(bp.name)
& FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
- boolean restrictionApplied = (origPermissions.getPermissionFlags(
- bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+ boolean restrictionApplied = (origState.getPermissionFlags(
+ bp.name) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
if (appSupportsRuntimePermissions) {
// If hard restricted we don't allow holding it
if (permissionPolicyInitialized && hardRestricted) {
if (!restrictionExempt) {
- if (permState != null && permState.isGranted()
- && permissionsState.revokeRuntimePermission(
- bp, userId) != PERMISSION_OPERATION_FAILURE) {
+ if (origPermState != null && origPermState.isGranted()
+ && uidState.revokePermission(
+ bp) != PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
if (!restrictionApplied) {
@@ -2882,15 +2869,15 @@
// Hard restricted permissions cannot be held.
} else if (!permissionPolicyInitialized
|| (!hardRestricted || restrictionExempt)) {
- if (permState != null && permState.isGranted()) {
- if (permissionsState.grantRuntimePermission(bp, userId)
+ if (origPermState != null && origPermState.isGranted()) {
+ if (uidState.grantPermission(bp)
== PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
}
}
} else {
- if (permState == null) {
+ if (origPermState == null) {
// New permission
if (PLATFORM_PACKAGE_NAME.equals(
bp.getSourcePackageName())) {
@@ -2902,8 +2889,8 @@
}
}
- if (!permissionsState.hasRuntimePermission(bp.name, userId)
- && permissionsState.grantRuntimePermission(bp, userId)
+ if (!uidState.hasPermission(bp.name)
+ && uidState.grantPermission(bp)
!= PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
@@ -2936,36 +2923,32 @@
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
}
- permissionsState.updatePermissionFlags(bp, userId,
- MASK_PERMISSION_FLAGS_ALL, flags);
- }
- } break;
+ uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL,
+ flags);
+ } break;
- case GRANT_UPGRADE: {
- // Upgrade from Pre-Q to Q permission model. Make all permissions
- // runtime
- PermissionState permState = origPermissions
- .getInstallPermissionState(perm);
- int flags = (permState != null) ? permState.getFlags() : 0;
+ case GRANT_UPGRADE: {
+ // Upgrade from Pre-Q to Q permission model. Make all permissions
+ // runtime
+ PermissionState origPermState = origState.getPermissionState(perm);
+ int flags = (origPermState != null) ? origPermState.getFlags() : 0;
- BasePermission bpToRevoke =
- upgradedActivityRecognitionPermission == null
- ? bp : mSettings.getPermissionLocked(
- upgradedActivityRecognitionPermission);
- // Remove install permission
- if (origPermissions.revokeInstallPermission(bpToRevoke)
- != PERMISSION_OPERATION_FAILURE) {
- origPermissions.updatePermissionFlags(bpToRevoke,
- UserHandle.USER_ALL,
- (MASK_PERMISSION_FLAGS_ALL
- & ~FLAG_PERMISSION_APPLY_RESTRICTION), 0);
- changedInstallPermission = true;
- }
+ BasePermission bpToRevoke =
+ upgradedActivityRecognitionPermission == null
+ ? bp : mSettings.getPermissionLocked(
+ upgradedActivityRecognitionPermission);
+ // Remove install permission
+ if (origState.revokePermission(bpToRevoke)
+ != PERMISSION_OPERATION_FAILURE) {
+ origState.updatePermissionFlags(bpToRevoke,
+ (MASK_PERMISSION_FLAGS_ALL
+ & ~FLAG_PERMISSION_APPLY_RESTRICTION), 0);
+ changedInstallPermission = true;
+ }
- boolean hardRestricted = bp.isHardRestricted();
- boolean softRestricted = bp.isSoftRestricted();
+ boolean hardRestricted = bp.isHardRestricted();
+ boolean softRestricted = bp.isSoftRestricted();
- for (int userId : userIds) {
// If permission policy is not ready we don't deal with restricted
// permissions as the policy may whitelist some permissions. Once
// the policy is initialized we would re-evaluate permissions.
@@ -2976,18 +2959,18 @@
boolean wasChanged = false;
boolean restrictionExempt =
- (origPermissions.getPermissionFlags(bp.name, userId)
+ (origState.getPermissionFlags(bp.name)
& FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
- boolean restrictionApplied = (origPermissions.getPermissionFlags(
- bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+ boolean restrictionApplied = (origState.getPermissionFlags(
+ bp.name) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
if (appSupportsRuntimePermissions) {
// If hard restricted we don't allow holding it
if (permissionPolicyInitialized && hardRestricted) {
if (!restrictionExempt) {
- if (permState != null && permState.isGranted()
- && permissionsState.revokeRuntimePermission(
- bp, userId) != PERMISSION_OPERATION_FAILURE) {
+ if (origPermState != null && origPermState.isGranted()
+ && uidState.revokePermission(
+ bp) != PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
if (!restrictionApplied) {
@@ -3017,15 +3000,15 @@
// Hard restricted permissions cannot be held.
} else if (!permissionPolicyInitialized ||
(!hardRestricted || restrictionExempt)) {
- if (permissionsState.grantRuntimePermission(bp, userId) !=
- PERMISSION_OPERATION_FAILURE) {
+ if (uidState.grantPermission(bp)
+ != PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
}
} else {
- if (!permissionsState.hasRuntimePermission(bp.name, userId)
- && permissionsState.grantRuntimePermission(bp,
- userId) != PERMISSION_OPERATION_FAILURE) {
+ if (!uidState.hasPermission(bp.name)
+ && uidState.grantPermission(bp)
+ != PERMISSION_OPERATION_FAILURE) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
wasChanged = true;
}
@@ -3058,71 +3041,74 @@
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
}
- permissionsState.updatePermissionFlags(bp, userId,
+ uidState.updatePermissionFlags(bp,
MASK_PERMISSION_FLAGS_ALL, flags);
- }
- } break;
+ } break;
- default: {
- if (packageOfInterest == null
- || packageOfInterest.equals(pkg.getPackageName())) {
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, "Not granting permission " + perm
- + " to package " + pkg.getPackageName()
- + " because it was previously installed without");
+ default: {
+ if (packageOfInterest == null
+ || packageOfInterest.equals(pkg.getPackageName())) {
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Not granting permission " + perm
+ + " to package " + friendlyName
+ + " because it was previously installed without");
+ }
}
- }
- } break;
- }
- } else {
- if (permissionsState.revokeInstallPermission(bp) !=
- PERMISSION_OPERATION_FAILURE) {
- // Also drop the permission flags.
- permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
- MASK_PERMISSION_FLAGS_ALL, 0);
- changedInstallPermission = true;
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, "Un-granting permission " + perm
- + " from package " + pkg.getPackageName()
- + " (protectionLevel=" + bp.getProtectionLevel()
- + " flags=0x"
- + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, ps))
- + ")");
+ } break;
}
- } else if (bp.isAppOp()) {
- // Don't print warning for app op permissions, since it is fine for them
- // not to be granted, there is a UI for the user to decide.
- if (DEBUG_PERMISSIONS
- && (packageOfInterest == null
- || packageOfInterest.equals(pkg.getPackageName()))) {
- Slog.i(TAG, "Not granting permission " + perm
- + " to package " + pkg.getPackageName()
- + " (protectionLevel=" + bp.getProtectionLevel()
- + " flags=0x"
- + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, ps))
- + ")");
+ } else {
+ if (uidState.revokePermission(bp) != PERMISSION_OPERATION_FAILURE) {
+ // Also drop the permission flags.
+ uidState.updatePermissionFlags(bp,
+ MASK_PERMISSION_FLAGS_ALL, 0);
+ changedInstallPermission = true;
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Un-granting permission " + perm
+ + " from package " + friendlyName
+ + " (protectionLevel=" + bp.getProtectionLevel()
+ + " flags=0x"
+ + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg,
+ ps))
+ + ")");
+ }
+ } else if (bp.isAppOp()) {
+ // Don't print warning for app op permissions, since it is fine for them
+ // not to be granted, there is a UI for the user to decide.
+ if (DEBUG_PERMISSIONS
+ && (packageOfInterest == null
+ || packageOfInterest.equals(pkg.getPackageName()))) {
+ Slog.i(TAG, "Not granting permission " + perm
+ + " to package " + friendlyName
+ + " (protectionLevel=" + bp.getProtectionLevel()
+ + " flags=0x"
+ + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg,
+ ps))
+ + ")");
+ }
}
}
}
}
+
+ if ((changedInstallPermission || replace)
+ && !userState.areInstallPermissionsFixed(ps.name)
+ && !ps.isSystem() || ps.getPkgState().isUpdatedSystemApp()) {
+ // This is the first that we have heard about this package, so the
+ // permissions we have now selected are fixed until explicitly
+ // changed.
+ userState.setInstallPermissionsFixed(ps.name, true);
+ }
+
+ synchronized (mLock) {
+ updatedUserIds = revokePermissionsNoLongerImplicitLocked(uidState, pkg,
+ userId, updatedUserIds);
+ updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origState,
+ uidState, pkg, newImplicitPermissions, userId, updatedUserIds);
+ }
}
- if ((changedInstallPermission || replace) && !ps.areInstallPermissionsFixed() &&
- !ps.isSystem() || ps.getPkgState().isUpdatedSystemApp()) {
- // This is the first that we have heard about this package, so the
- // permissions we have now selected are fixed until explicitly
- // changed.
- ps.setInstallPermissionsFixed(true);
- }
-
- synchronized (mLock) {
- updatedUserIds = revokePermissionsNoLongerImplicitLocked(permissionsState, pkg,
- userIds, updatedUserIds);
- updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origPermissions,
- permissionsState, pkg, newImplicitPermissions, userIds, updatedUserIds);
- updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, userIds,
- updatedUserIds);
- }
+ updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, userIds,
+ updatedUserIds);
// TODO: Kill UIDs whose GIDs or runtime permissions changed. This might be more important
// for shared users.
@@ -3159,40 +3145,38 @@
*
* @return The updated value of the {@code updatedUserIds} parameter
*/
- private @NonNull int[] revokePermissionsNoLongerImplicitLocked(@NonNull PermissionsState ps,
- @NonNull AndroidPackage pkg, @NonNull int[] userIds, @NonNull int[] updatedUserIds) {
+ private @NonNull int[] revokePermissionsNoLongerImplicitLocked(@NonNull UidPermissionState ps,
+ @NonNull AndroidPackage pkg, int userId, @NonNull int[] updatedUserIds) {
String pkgName = pkg.getPackageName();
boolean supportsRuntimePermissions = pkg.getTargetSdkVersion()
>= Build.VERSION_CODES.M;
- for (int userId : userIds) {
- for (String permission : ps.getPermissions(userId)) {
- if (!pkg.getImplicitPermissions().contains(permission)) {
- if (!ps.hasInstallPermission(permission)) {
- int flags = ps.getRuntimePermissionState(permission, userId).getFlags();
+ for (String permission : ps.getPermissions()) {
+ if (!pkg.getImplicitPermissions().contains(permission)) {
+ if (!ps.hasInstallPermission(permission)) {
+ int flags = ps.getPermissionFlags(permission);
- if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
- BasePermission bp = mSettings.getPermissionLocked(permission);
+ if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
+ BasePermission bp = mSettings.getPermissionLocked(permission);
- int flagsToRemove = FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
+ int flagsToRemove = FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
- if ((flags & BLOCKING_PERMISSION_FLAGS) == 0
- && supportsRuntimePermissions) {
- int revokeResult = ps.revokeRuntimePermission(bp, userId);
- if (revokeResult != PERMISSION_OPERATION_FAILURE) {
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, "Revoking runtime permission "
- + permission + " for " + pkgName
- + " as it is now requested");
- }
+ if ((flags & BLOCKING_PERMISSION_FLAGS) == 0
+ && supportsRuntimePermissions) {
+ int revokeResult = ps.revokePermission(bp);
+ if (revokeResult != PERMISSION_OPERATION_FAILURE) {
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Revoking runtime permission "
+ + permission + " for " + pkgName
+ + " as it is now requested");
}
-
- flagsToRemove |= USER_PERMISSION_FLAGS;
}
- ps.updatePermissionFlags(bp, userId, flagsToRemove, 0);
- updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+ flagsToRemove |= USER_PERMISSION_FLAGS;
}
+
+ ps.updatePermissionFlags(bp, flagsToRemove, 0);
+ updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
}
}
}
@@ -3213,12 +3197,10 @@
* @param newPerm The permission to inherit to
* @param ps The permission state of the package
* @param pkg The package requesting the permissions
- * @param userId The user the permission belongs to
*/
private void inheritPermissionStateToNewImplicitPermissionLocked(
@NonNull ArraySet<String> sourcePerms, @NonNull String newPerm,
- @NonNull PermissionsState ps, @NonNull AndroidPackage pkg,
- @UserIdInt int userId) {
+ @NonNull UidPermissionState ps, @NonNull AndroidPackage pkg) {
String pkgName = pkg.getPackageName();
boolean isGranted = false;
int flags = 0;
@@ -3226,17 +3208,16 @@
int numSourcePerm = sourcePerms.size();
for (int i = 0; i < numSourcePerm; i++) {
String sourcePerm = sourcePerms.valueAt(i);
- if ((ps.hasRuntimePermission(sourcePerm, userId))
- || ps.hasInstallPermission(sourcePerm)) {
+ if (ps.hasPermission(sourcePerm)) {
if (!isGranted) {
flags = 0;
}
isGranted = true;
- flags |= ps.getPermissionFlags(sourcePerm, userId);
+ flags |= ps.getPermissionFlags(sourcePerm);
} else {
if (!isGranted) {
- flags |= ps.getPermissionFlags(sourcePerm, userId);
+ flags |= ps.getPermissionFlags(sourcePerm);
}
}
}
@@ -3247,11 +3228,11 @@
+ " for " + pkgName);
}
- ps.grantRuntimePermission(mSettings.getPermissionLocked(newPerm), userId);
+ ps.grantPermission(mSettings.getPermissionLocked(newPerm));
}
// Add permission flags
- ps.updatePermissionFlags(mSettings.getPermission(newPerm), userId, flags, flags);
+ ps.updatePermissionFlags(mSettings.getPermission(newPerm), flags, flags);
}
/**
@@ -3283,15 +3264,15 @@
* @param origPs The permission state of the package before the split
* @param ps The new permission state
* @param pkg The package the permission belongs to
- * @param userIds All user IDs in the system, must be passed in because this method is locked
+ * @param userId The user ID
* @param updatedUserIds List of users for which the permission state has already been changed
*
* @return List of users for which the permission state has been changed
*/
private @NonNull int[] setInitialGrantForNewImplicitPermissionsLocked(
- @NonNull PermissionsState origPs, @NonNull PermissionsState ps,
+ @NonNull UidPermissionState origPs, @NonNull UidPermissionState ps,
@NonNull AndroidPackage pkg, @NonNull ArraySet<String> newImplicitPermissions,
- @NonNull int[] userIds, @NonNull int[] updatedUserIds) {
+ @UserIdInt int userId, @NonNull int[] updatedUserIds) {
String pkgName = pkg.getPackageName();
ArrayMap<String, ArraySet<String>> newToSplitPerms = new ArrayMap<>();
@@ -3325,35 +3306,33 @@
if (!ps.hasInstallPermission(newPerm)) {
BasePermission bp = mSettings.getPermissionLocked(newPerm);
- for (int userId : userIds) {
- if (!newPerm.equals(Manifest.permission.ACTIVITY_RECOGNITION)) {
- ps.updatePermissionFlags(bp, userId,
- FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
- FLAG_PERMISSION_REVOKE_WHEN_REQUESTED);
- }
- updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+ if (!newPerm.equals(Manifest.permission.ACTIVITY_RECOGNITION)) {
+ ps.updatePermissionFlags(bp,
+ FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
+ FLAG_PERMISSION_REVOKE_WHEN_REQUESTED);
+ }
+ updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
- boolean inheritsFromInstallPerm = false;
- for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size();
- sourcePermNum++) {
- if (ps.hasInstallPermission(sourcePerms.valueAt(sourcePermNum))) {
- inheritsFromInstallPerm = true;
- break;
- }
+ boolean inheritsFromInstallPerm = false;
+ for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size();
+ sourcePermNum++) {
+ if (ps.hasInstallPermission(sourcePerms.valueAt(sourcePermNum))) {
+ inheritsFromInstallPerm = true;
+ break;
}
+ }
- if (!origPs.hasRequestedPermission(sourcePerms)
- && !inheritsFromInstallPerm) {
- // Both permissions are new so nothing to inherit.
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, newPerm + " does not inherit from " + sourcePerms
- + " for " + pkgName + " as split permission is also new");
- }
- } else {
- // Inherit from new install or existing runtime permissions
- inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms,
- newPerm, ps, pkg, userId);
+ if (!origPs.hasRequestedPermission(sourcePerms)
+ && !inheritsFromInstallPerm) {
+ // Both permissions are new so nothing to inherit.
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, newPerm + " does not inherit from " + sourcePerms
+ + " for " + pkgName + " as split permission is also new");
}
+ } else {
+ // Inherit from new install or existing runtime permissions
+ inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms,
+ newPerm, ps, pkg);
}
}
}
@@ -3484,7 +3463,7 @@
}
private boolean shouldGrantSignaturePermission(String perm, AndroidPackage pkg,
- PackageSetting pkgSetting, BasePermission bp, PermissionsState origPermissions) {
+ PackageSetting pkgSetting, BasePermission bp, UidPermissionState origPermissions) {
boolean oemPermission = bp.isOEM();
boolean vendorPrivilegedPermission = bp.isVendorPrivileged();
boolean privilegedPermission = bp.isPrivileged() || bp.isVendorPrivileged();
@@ -3760,12 +3739,13 @@
}
// Legacy apps have the permission and get user consent on launch.
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
return false;
}
- return permissionsState.isPermissionReviewRequired(userId);
+ return uidState.isPermissionReviewRequired();
}
private boolean isPackageRequestingPermission(AndroidPackage pkg, String permission) {
@@ -3789,9 +3769,10 @@
private void grantRequestedRuntimePermissionsForUser(AndroidPackage pkg, int userId,
String[] grantedPermissions, int callingUid, PermissionCallback callback) {
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
return;
}
@@ -3816,7 +3797,7 @@
&& (supportsRuntimePermissions || !bp.isRuntimeOnly())
&& (grantedPermissions == null
|| ArrayUtils.contains(grantedPermissions, permission))) {
- final int flags = permissionsState.getPermissionFlags(permission, userId);
+ final int flags = uidState.getPermissionFlags(permission);
if (supportsRuntimePermissions) {
// Installer cannot change immutable permissions.
if ((flags & immutableFlags) == 0) {
@@ -3838,18 +3819,19 @@
private void setWhitelistedRestrictedPermissionsForUsers(@NonNull AndroidPackage pkg,
@UserIdInt int[] userIds, @Nullable List<String> permissions, int callingUid,
@PermissionWhitelistFlags int whitelistFlags, PermissionCallback callback) {
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
- return;
- }
-
SparseArray<ArraySet<String>> oldGrantedRestrictedPermissions = new SparseArray<>();
boolean updatePermissions = false;
final int permissionCount = pkg.getRequestedPermissions().size();
for (int i = 0; i < userIds.length; i++) {
int userId = userIds[i];
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
+ continue;
+ }
+
for (int j = 0; j < permissionCount; j++) {
final String permissionName = pkg.getRequestedPermissions().get(j);
@@ -3859,14 +3841,14 @@
continue;
}
- if (permissionsState.hasPermission(permissionName, userId)) {
+ if (uidState.hasPermission(permissionName)) {
if (oldGrantedRestrictedPermissions.get(userId) == null) {
oldGrantedRestrictedPermissions.put(userId, new ArraySet<>());
}
oldGrantedRestrictedPermissions.get(userId).add(permissionName);
}
- final int oldFlags = permissionsState.getPermissionFlags(permissionName, userId);
+ final int oldFlags = uidState.getPermissionFlags(permissionName);
int newFlags = oldFlags;
int mask = 0;
@@ -3921,8 +3903,7 @@
// as whitelisting trumps policy i.e. policy cannot grant a non
// grantable permission.
if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
- final boolean isGranted = permissionsState.hasPermission(permissionName,
- userId);
+ final boolean isGranted = uidState.hasPermission(permissionName);
if (!isWhitelisted && isGranted) {
mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED;
@@ -3958,12 +3939,13 @@
for (int j = 0; j < oldGrantedCount; j++) {
final String permission = oldPermsForUser.valueAt(j);
// Sometimes we create a new permission state instance during update.
- final PermissionsState newPermissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState newUidState = getUidState(pkg, userId);
+ if (newUidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()
+ + " and user " + userId);
continue;
}
- if (!newPermissionsState.hasPermission(permission, userId)) {
+ if (!newUidState.hasPermission(permission)) {
callback.onPermissionRevoked(pkg.getUid(), userId, null);
break;
}
@@ -4012,9 +3994,10 @@
continue;
}
- PermissionsState permissionsState = getPermissionsState(deletedPs.pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName());
+ UidPermissionState uidState = getUidState(deletedPs.pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName()
+ + " and user " + userId);
continue;
}
@@ -4036,25 +4019,15 @@
}
}
- // Try to revoke as an install permission which is for all users.
// The package is gone - no need to keep flags for applying policy.
- permissionsState.updatePermissionFlags(bp, userId,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
-
- if (permissionsState.revokeInstallPermission(bp)
- == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
- affectedUserId = UserHandle.USER_ALL;
- }
+ uidState.updatePermissionFlags(bp, PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
// Try to revoke as a runtime permission which is per user.
- if (permissionsState.revokeRuntimePermission(bp, userId)
- == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
- if (affectedUserId == UserHandle.USER_NULL) {
- affectedUserId = userId;
- } else if (affectedUserId != userId) {
- // Multiple users affected.
- affectedUserId = UserHandle.USER_ALL;
- }
+ // 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)
+ == UidPermissionState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
+ affectedUserId = userId;
}
}
@@ -4062,12 +4035,12 @@
}
@GuardedBy("mLock")
- private int[] revokeUnusedSharedUserPermissionsLocked(
- List<AndroidPackage> pkgList, PermissionsState permissionsState, int[] allUserIds) {
+ private boolean revokeUnusedSharedUserPermissionsLocked(
+ List<AndroidPackage> pkgList, UidPermissionState uidState) {
// Collect all used permissions in the UID
final ArraySet<String> usedPermissions = new ArraySet<>();
if (pkgList == null || pkgList.size() == 0) {
- return EmptyArray.INT;
+ return false;
}
for (AndroidPackage pkg : pkgList) {
if (pkg.getRequestedPermissions().isEmpty()) {
@@ -4083,44 +4056,27 @@
}
}
- // Prune install permissions
- List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates();
- final int installPermCount = installPermStates.size();
- for (int i = installPermCount - 1; i >= 0; i--) {
- PermissionState permissionState = installPermStates.get(i);
+ boolean runtimePermissionChanged = false;
+
+ // Prune permissions
+ final List<com.android.server.pm.permission.PermissionState> permissionStates =
+ uidState.getPermissionStates();
+ final int permissionStatesSize = permissionStates.size();
+ for (int i = permissionStatesSize - 1; i >= 0; i--) {
+ PermissionState permissionState = permissionStates.get(i);
if (!usedPermissions.contains(permissionState.getName())) {
BasePermission bp = mSettings.getPermissionLocked(permissionState.getName());
if (bp != null) {
- permissionsState.revokeInstallPermission(bp);
- permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
- MASK_PERMISSION_FLAGS_ALL, 0);
- }
- }
- }
-
- int[] runtimePermissionChangedUserIds = EmptyArray.INT;
-
- // Prune runtime permissions
- for (int userId : allUserIds) {
- List<PermissionState> runtimePermStates = permissionsState
- .getRuntimePermissionStates(userId);
- final int runtimePermCount = runtimePermStates.size();
- for (int i = runtimePermCount - 1; i >= 0; i--) {
- PermissionState permissionState = runtimePermStates.get(i);
- if (!usedPermissions.contains(permissionState.getName())) {
- BasePermission bp = mSettings.getPermissionLocked(permissionState.getName());
- if (bp != null) {
- permissionsState.revokeRuntimePermission(bp, userId);
- permissionsState.updatePermissionFlags(bp, userId,
- MASK_PERMISSION_FLAGS_ALL, 0);
- runtimePermissionChangedUserIds = ArrayUtils.appendInt(
- runtimePermissionChangedUserIds, userId);
+ uidState.revokePermission(bp);
+ uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL, 0);
+ if (permissionState.isRuntime()) {
+ runtimePermissionChanged = true;
}
}
}
}
- return runtimePermissionChangedUserIds;
+ return runtimePermissionChanged;
}
/**
@@ -4368,15 +4324,19 @@
}
} else {
mPackageManagerInt.forEachPackage(p -> {
- final PermissionsState permissionsState = getPermissionsState(p);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + p.getPackageName());
- return;
- }
- if (permissionsState.getInstallPermissionState(bp.getName()) != null) {
- permissionsState.revokeInstallPermission(bp);
- permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
- MASK_PERMISSION_FLAGS_ALL, 0);
+ final int[] userIds = mUserManagerInt.getUserIds();
+ for (final int userId : userIds) {
+ final UidPermissionState uidState = getUidState(p, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for "
+ + p.getPackageName() + " and user " + userId);
+ return;
+ }
+ if (uidState.getPermissionState(bp.getName()) != null) {
+ uidState.revokePermission(bp);
+ uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL,
+ 0);
+ }
}
});
}
@@ -4784,62 +4744,124 @@
}
@Nullable
- private PermissionsState getPermissionsState(@NonNull PackageSetting ps) {
- return getPermissionsState(ps.getAppId());
+ private UidPermissionState getUidState(@NonNull PackageSetting ps,
+ @UserIdInt int userId) {
+ return getUidState(ps.getAppId(), userId);
}
@Nullable
- private PermissionsState getPermissionsState(@NonNull AndroidPackage pkg) {
- return getPermissionsState(pkg.getUid());
+ private UidPermissionState getUidState(@NonNull AndroidPackage pkg,
+ @UserIdInt int userId) {
+ return getUidState(pkg.getUid(), userId);
}
@Nullable
- private PermissionsState getPermissionsState(int appId) {
+ private UidPermissionState getUidState(int appId, @UserIdInt int userId) {
synchronized (mLock) {
- return mAppIdStates.get(appId);
- }
- }
-
- @Nullable
- private PermissionsState getOrCreatePermissionsState(@NonNull PackageSetting ps) {
- return getOrCreatePermissionsState(ps.getAppId());
- }
-
- @Nullable
- private PermissionsState getOrCreatePermissionsState(int appId) {
- synchronized (mLock) {
- PermissionsState state = mAppIdStates.get(appId);
- if (state == null) {
- state = new PermissionsState();
- mAppIdStates.put(appId, state);
+ final UserPermissionState userState = mState.getUserState(userId);
+ if (userState == null) {
+ return null;
}
- return state;
+ return userState.getUidState(appId);
}
}
- private void removePermissionsState(int appId) {
+ private void removeAppState(int appId) {
synchronized (mLock) {
- mAppIdStates.remove(appId);
+ final int[] userIds = mState.getUserIds();
+ for (final int userId : userIds) {
+ final UserPermissionState userState = mState.getUserState(userId);
+ userState.removeUidState(appId);
+ }
}
}
- private void readPermissionsStateFromPackageSettings() {
+ private void readStateFromPackageSettings() {
+ final int[] userIds = getAllUserIds();
mPackageManagerInt.forEachPackageSetting(ps -> {
+ final int appId = ps.getAppId();
+ final PermissionsState permissionsState = ps.getPermissionsState();
+
synchronized (mLock) {
- mAppIdStates.put(ps.getAppId(), new PermissionsState(ps.getPermissionsState()));
+ for (final int userId : userIds) {
+ final UserPermissionState userState = mState.getOrCreateUserState(userId);
+
+ userState.setInstallPermissionsFixed(ps.name, ps.areInstallPermissionsFixed());
+ final UidPermissionState uidState = userState.getOrCreateUidState(appId);
+ uidState.reset();
+ uidState.setGlobalGids(permissionsState.getGlobalGids());
+ uidState.setMissing(permissionsState.isMissing(userId));
+ readStateFromPermissionStates(uidState,
+ permissionsState.getInstallPermissionStates(), false);
+ readStateFromPermissionStates(uidState,
+ permissionsState.getRuntimePermissionStates(userId), true);
+ }
}
});
}
- private void writePermissionsStateToPackageSettings() {
+ private void readStateFromPermissionStates(@NonNull UidPermissionState uidState,
+ @NonNull List<PermissionsState.PermissionState> permissionStates, boolean isRuntime) {
+ final int permissionStatesSize = permissionStates.size();
+ for (int i = 0; i < permissionStatesSize; i++) {
+ final PermissionsState.PermissionState permissionState = permissionStates.get(i);
+ final BasePermission permission = permissionState.getPermission();
+ uidState.putPermissionState(permission, isRuntime, permissionState.isGranted(),
+ permissionState.getFlags());
+ }
+ }
+
+ private void writeStateToPackageSettings() {
+ final int[] userIds = mState.getUserIds();
mPackageManagerInt.forEachPackageSetting(ps -> {
+ ps.setInstallPermissionsFixed(false);
+ final PermissionsState permissionsState = ps.getPermissionsState();
+ permissionsState.reset();
+ final int appId = ps.getAppId();
+
synchronized (mLock) {
- final PermissionsState permissionsState = mAppIdStates.get(ps.getAppId());
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + ps.name);
- return;
+ for (final int userId : userIds) {
+ final UserPermissionState userState = mState.getUserState(userId);
+ if (userState == null) {
+ Slog.e(TAG, "Missing user state for " + userId);
+ continue;
+ }
+
+ if (userState.areInstallPermissionsFixed(ps.name)) {
+ ps.setInstallPermissionsFixed(true);
+ }
+
+ final UidPermissionState uidState = userState.getUidState(appId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permission state for " + ps.name + " and user "
+ + userId);
+ continue;
+ }
+
+ permissionsState.setGlobalGids(uidState.getGlobalGids());
+ permissionsState.setMissing(uidState.isMissing(), userId);
+ final List<PermissionState> permissionStates = uidState.getPermissionStates();
+ final int permissionStatesSize = permissionStates.size();
+ for (int i = 0; i < permissionStatesSize; i++) {
+ final PermissionState permissionState = permissionStates.get(i);
+
+ final BasePermission permission = permissionState.getPermission();
+ if (permissionState.isGranted()) {
+ if (permissionState.isRuntime()) {
+ permissionsState.grantRuntimePermission(permission, userId);
+ } else {
+ permissionsState.grantInstallPermission(permission);
+ }
+ }
+ final int flags = permissionState.getFlags();
+ if (flags != 0) {
+ final int flagsUserId = permissionState.isRuntime() ? userId
+ : UserHandle.USER_ALL;
+ permissionsState.updatePermissionFlags(permission, flagsUserId, flags,
+ flags);
+ }
+ }
}
- ps.getPermissionsState().copyFrom(permissionsState);
}
});
}
@@ -4876,12 +4898,12 @@
PermissionManagerService.this.removeAllPermissions(pkg, chatty);
}
@Override
- public void readPermissionsStateFromPackageSettingsTEMP() {
- PermissionManagerService.this.readPermissionsStateFromPackageSettings();
+ public void readStateFromPackageSettingsTEMP() {
+ PermissionManagerService.this.readStateFromPackageSettings();
}
@Override
- public void writePermissionsStateToPackageSettingsTEMP() {
- PermissionManagerService.this.writePermissionsStateToPackageSettings();
+ public void writeStateToPackageSettingsTEMP() {
+ PermissionManagerService.this.writeStateToPackageSettings();
}
@Override
public void onUserRemoved(@UserIdInt int userId) {
@@ -4889,7 +4911,7 @@
}
@Override
public void removePermissionsStateTEMP(int appId) {
- PermissionManagerService.this.removePermissionsState(appId);
+ PermissionManagerService.this.removeAppState(appId);
}
@Override
@UserIdInt
@@ -5222,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 ca207d8..6d9bd2a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -266,21 +266,21 @@
public abstract void removeAllPermissions(@NonNull AndroidPackage pkg, boolean chatty);
/**
- * Read {@code PermissionsState} from package settings.
+ * Read permission state from package settings.
*
* TODO(zhanghai): This is a temporary method because we should not expose
* {@code PackageSetting} which is a implementation detail that permission should not know.
* Instead, it should retrieve the legacy state via a defined API.
*/
- public abstract void readPermissionsStateFromPackageSettingsTEMP();
+ public abstract void readStateFromPackageSettingsTEMP();
/**
- * Write {@code PermissionsState} from to settings.
+ * Write permission state to package settings.
*
* TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence
* for permission.
*/
- public abstract void writePermissionsStateToPackageSettingsTEMP();
+ public abstract void writeStateToPackageSettingsTEMP();
/**
* Notify that a user has been removed and its permission state should be removed as well.
diff --git a/services/core/java/com/android/server/pm/permission/PermissionState.java b/services/core/java/com/android/server/pm/permission/PermissionState.java
new file mode 100644
index 0000000..2ed9a50
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/PermissionState.java
@@ -0,0 +1,129 @@
+/*
+ * 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.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * State for a single permission.
+ */
+public final class PermissionState {
+
+ @NonNull
+ private final BasePermission mPermission;
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private boolean mRuntime;
+
+ @GuardedBy("mLock")
+ private boolean mGranted;
+
+ @GuardedBy("mLock")
+ private int mFlags;
+
+ public PermissionState(@NonNull BasePermission permission, boolean isRuntime) {
+ mPermission = permission;
+ mRuntime = isRuntime;
+ }
+
+ public PermissionState(@NonNull PermissionState other) {
+ this(other.mPermission, other.mRuntime);
+
+ mGranted = other.mGranted;
+ mFlags = other.mFlags;
+ }
+
+ @NonNull
+ public BasePermission getPermission() {
+ return mPermission;
+ }
+
+ @NonNull
+ public String getName() {
+ return mPermission.getName();
+ }
+
+ @Nullable
+ public int[] computeGids(@UserIdInt int userId) {
+ return mPermission.computeGids(userId);
+ }
+
+ public boolean isRuntime() {
+ synchronized (mLock) {
+ return mRuntime;
+ }
+ }
+
+ public boolean isGranted() {
+ synchronized (mLock) {
+ return mGranted;
+ }
+ }
+
+ public boolean grant() {
+ synchronized (mLock) {
+ if (mGranted) {
+ return false;
+ }
+ mGranted = true;
+ UidPermissionState.invalidateCache();
+ return true;
+ }
+ }
+
+ public boolean revoke() {
+ synchronized (mLock) {
+ if (!mGranted) {
+ return false;
+ }
+ mGranted = false;
+ UidPermissionState.invalidateCache();
+ return true;
+ }
+ }
+
+ public int getFlags() {
+ synchronized (mLock) {
+ return mFlags;
+ }
+ }
+
+ public boolean updateFlags(int flagMask, int flagValues) {
+ synchronized (mLock) {
+ final int newFlags = flagValues & flagMask;
+
+ // Okay to do before the modification because we hold the lock.
+ UidPermissionState.invalidateCache();
+
+ final int oldFlags = mFlags;
+ mFlags = (mFlags & ~flagMask) | newFlags;
+ return mFlags != oldFlags;
+ }
+ }
+
+ public boolean isDefault() {
+ synchronized (mLock) {
+ return !mGranted && mFlags == 0;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionsState.java b/services/core/java/com/android/server/pm/permission/PermissionsState.java
index bad59cb..4fb2d5f 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionsState.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionsState.java
@@ -86,6 +86,10 @@
copyFrom(prototype);
}
+ public int[] getGlobalGids() {
+ return mGlobalGids;
+ }
+
/**
* Sets the global gids, applicable to all users.
*
@@ -825,7 +829,7 @@
PermissionState userState = mUserStates.get(userId);
if (userState == null) {
- userState = new PermissionState(mPerm.getName());
+ userState = new PermissionState(mPerm);
mUserStates.put(userId, userState);
}
@@ -908,7 +912,7 @@
}
return userState.mFlags != oldFlags;
} else if (newFlags != 0) {
- userState = new PermissionState(mPerm.getName());
+ userState = new PermissionState(mPerm);
userState.mFlags = newFlags;
mUserStates.put(userId, userState);
return true;
@@ -929,16 +933,16 @@
}
public static final class PermissionState {
- private final String mName;
+ private final BasePermission mPermission;
private boolean mGranted;
private int mFlags;
- public PermissionState(String name) {
- mName = name;
+ public PermissionState(BasePermission permission) {
+ mPermission = permission;
}
public PermissionState(PermissionState other) {
- mName = other.mName;
+ mPermission = other.mPermission;
mGranted = other.mGranted;
mFlags = other.mFlags;
}
@@ -947,8 +951,12 @@
return !mGranted && mFlags == 0;
}
+ public BasePermission getPermission() {
+ return mPermission;
+ }
+
public String getName() {
- return mName;
+ return mPermission.getName();
}
public boolean isGranted() {
diff --git a/services/core/java/com/android/server/pm/permission/UidPermissionState.java b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
new file mode 100644
index 0000000..4c047ff
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.PackageManager;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Permission state for a UID.
+ * <p>
+ * This class is also responsible for keeping track of the Linux GIDs per
+ * user for a package or a shared user. The GIDs are computed as a set of
+ * the GIDs for all granted permissions' GIDs on a per user basis.
+ */
+public final class UidPermissionState {
+ /** The permission operation failed. */
+ public static final int PERMISSION_OPERATION_FAILURE = -1;
+
+ /** The permission operation succeeded and no gids changed. */
+ public static final int PERMISSION_OPERATION_SUCCESS = 0;
+
+ /** The permission operation succeeded and gids changed. */
+ public static final int PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED = 1;
+
+ private static final int[] NO_GIDS = {};
+
+ @NonNull
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private ArrayMap<String, PermissionState> mPermissions;
+
+ @NonNull
+ private int[] mGlobalGids = NO_GIDS;
+
+ private boolean mMissing;
+
+ private boolean mPermissionReviewRequired;
+
+ public UidPermissionState() {
+ /* do nothing */
+ }
+
+ public UidPermissionState(@NonNull UidPermissionState prototype) {
+ copyFrom(prototype);
+ }
+
+ /**
+ * Gets the global gids, applicable to all users.
+ */
+ @NonNull
+ public int[] getGlobalGids() {
+ return mGlobalGids;
+ }
+
+ /**
+ * Sets the global gids, applicable to all users.
+ *
+ * @param globalGids The global gids.
+ */
+ public void setGlobalGids(@NonNull int[] globalGids) {
+ if (!ArrayUtils.isEmpty(globalGids)) {
+ mGlobalGids = Arrays.copyOf(globalGids, globalGids.length);
+ }
+ }
+
+ static void invalidateCache() {
+ PackageManager.invalidatePackageInfoCache();
+ }
+
+ /**
+ * Initialized this instance from another one.
+ *
+ * @param other The other instance.
+ */
+ public void copyFrom(@NonNull UidPermissionState other) {
+ if (other == this) {
+ return;
+ }
+
+ synchronized (mLock) {
+ if (mPermissions != null) {
+ if (other.mPermissions == null) {
+ mPermissions = null;
+ } else {
+ mPermissions.clear();
+ }
+ }
+ if (other.mPermissions != null) {
+ if (mPermissions == null) {
+ mPermissions = new ArrayMap<>();
+ }
+ final int permissionCount = other.mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ String name = other.mPermissions.keyAt(i);
+ PermissionState permissionState = other.mPermissions.valueAt(i);
+ mPermissions.put(name, new PermissionState(permissionState));
+ }
+ }
+ }
+
+ mGlobalGids = NO_GIDS;
+ if (other.mGlobalGids != NO_GIDS) {
+ mGlobalGids = other.mGlobalGids.clone();
+ }
+
+ mMissing = other.mMissing;
+
+ mPermissionReviewRequired = other.mPermissionReviewRequired;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final UidPermissionState other = (UidPermissionState) obj;
+
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ if (other.mPermissions != null) {
+ return false;
+ }
+ } else if (!mPermissions.equals(other.mPermissions)) {
+ return false;
+ }
+ }
+
+ if (mMissing != other.mMissing) {
+ return false;
+ }
+
+ if (mPermissionReviewRequired != other.mPermissionReviewRequired) {
+ return false;
+ }
+ return Arrays.equals(mGlobalGids, other.mGlobalGids);
+ }
+
+ /**
+ * Check whether the permissions state is missing for a user. This can happen if permission
+ * state is rolled back and we'll need to generate a reasonable default state to keep the app
+ * usable.
+ */
+ public boolean isMissing() {
+ return mMissing;
+ }
+
+ /**
+ * Set whether the permissions state is missing for a user. This can happen if permission state
+ * is rolled back and we'll need to generate a reasonable default state to keep the app usable.
+ */
+ public void setMissing(boolean missing) {
+ mMissing = missing;
+ }
+
+ public boolean isPermissionReviewRequired() {
+ return mPermissionReviewRequired;
+ }
+
+ /**
+ * Gets whether the state has a given permission.
+ *
+ * @param name The permission name.
+ * @return Whether the state has the permission.
+ */
+ public boolean hasPermission(@NonNull String name) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return false;
+ }
+ PermissionState permissionState = mPermissions.get(name);
+ return permissionState != null && permissionState.isGranted();
+ }
+ }
+
+ /**
+ * Gets whether the state has a given install permission.
+ *
+ * @param name The permission name.
+ * @return Whether the state has the install permission.
+ */
+ public boolean hasInstallPermission(@NonNull String name) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return false;
+ }
+ PermissionState permissionState = mPermissions.get(name);
+ return permissionState != null && permissionState.isGranted()
+ && !permissionState.isRuntime();
+ }
+ }
+
+ /**
+ * Returns whether the state has any known request for the given permission name,
+ * whether or not it has been granted.
+ *
+ * @deprecated Not all requested permissions may be here.
+ */
+ @Deprecated
+ public boolean hasRequestedPermission(@NonNull ArraySet<String> names) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return false;
+ }
+ for (int i = names.size() - 1; i >= 0; i--) {
+ if (mPermissions.get(names.valueAt(i)) != null) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns whether the state has any known request for the given permission name,
+ * whether or not it has been granted.
+ *
+ * @deprecated Not all requested permissions may be here.
+ */
+ @Deprecated
+ public boolean hasRequestedPermission(@NonNull String name) {
+ return mPermissions != null && (mPermissions.get(name) != null);
+ }
+
+ /**
+ * Gets all permissions for a given device user id regardless if they
+ * are install time or runtime permissions.
+ *
+ * @return The permissions or an empty set.
+ */
+ @NonNull
+ public Set<String> getPermissions() {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return Collections.emptySet();
+ }
+
+ Set<String> permissions = new ArraySet<>(mPermissions.size());
+
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ String permission = mPermissions.keyAt(i);
+
+ if (hasPermission(permission)) {
+ permissions.add(permission);
+ }
+ }
+
+ return permissions;
+ }
+ }
+
+ /**
+ * Gets the flags for a permission.
+ *
+ * @param name The permission name.
+ * @return The permission state or null if no such.
+ */
+ public int getPermissionFlags(@NonNull String name) {
+ PermissionState permState = getPermissionState(name);
+ if (permState != null) {
+ return permState.getFlags();
+ }
+ return 0;
+ }
+
+ /**
+ * Update the flags associated with a given permission.
+ * @param permission The permission whose flags to update.
+ * @param flagMask Mask for which flags to change.
+ * @param flagValues New values for the mask flags.
+ * @return Whether the permission flags changed.
+ */
+ public boolean updatePermissionFlags(@NonNull BasePermission permission, int flagMask,
+ int flagValues) {
+ if (flagMask == 0) {
+ return false;
+ }
+
+ PermissionState permissionState = ensurePermissionState(permission);
+
+ final int oldFlags = permissionState.getFlags();
+
+ synchronized (mLock) {
+ final boolean updated = permissionState.updateFlags(flagMask, flagValues);
+ if (updated) {
+ final int newFlags = permissionState.getFlags();
+ if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0
+ && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ mPermissionReviewRequired = true;
+ } else if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0
+ && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
+ if (mPermissionReviewRequired && !hasPermissionRequiringReview()) {
+ mPermissionReviewRequired = false;
+ }
+ }
+ }
+ return updated;
+ }
+ }
+
+ private boolean hasPermissionRequiringReview() {
+ synchronized (mLock) {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ final PermissionState permission = mPermissions.valueAt(i);
+ if ((permission.getFlags() & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean updatePermissionFlagsForAllPermissions(int flagMask, int flagValues) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return false;
+ }
+ boolean changed = false;
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ PermissionState permissionState = mPermissions.valueAt(i);
+ changed |= permissionState.updateFlags(flagMask, flagValues);
+ }
+ return changed;
+ }
+ }
+
+ /**
+ * Compute the Linux gids for a given device user from the permissions
+ * granted to this user. Note that these are computed to avoid additional
+ * state as they are rarely accessed.
+ *
+ * @param userId The device user id.
+ * @return The gids for the device user.
+ */
+ @NonNull
+ public int[] computeGids(@UserIdInt int userId) {
+ int[] gids = mGlobalGids;
+
+ synchronized (mLock) {
+ if (mPermissions != null) {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ PermissionState permissionState = mPermissions.valueAt(i);
+ if (!permissionState.isGranted()) {
+ continue;
+ }
+ final int[] permGids = permissionState.computeGids(userId);
+ if (permGids != NO_GIDS) {
+ gids = appendInts(gids, permGids);
+ }
+ }
+ }
+ }
+
+ return gids;
+ }
+
+ /**
+ * Compute the Linux gids for all device users from the permissions
+ * granted to these users.
+ *
+ * @return The gids for all device users.
+ */
+ @NonNull
+ public int[] computeGids(@NonNull int[] userIds) {
+ int[] gids = mGlobalGids;
+
+ for (int userId : userIds) {
+ final int[] userGids = computeGids(userId);
+ gids = appendInts(gids, userGids);
+ }
+
+ return gids;
+ }
+
+ /**
+ * Resets the internal state of this object.
+ */
+ public void reset() {
+ mGlobalGids = NO_GIDS;
+
+ synchronized (mLock) {
+ mPermissions = null;
+ invalidateCache();
+ }
+
+ mMissing = false;
+ mPermissionReviewRequired = false;
+ }
+
+ /**
+ * Gets the state for a permission or null if no such.
+ *
+ * @param name The permission name.
+ * @return The permission state.
+ */
+ @Nullable
+ public PermissionState getPermissionState(@NonNull String name) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return null;
+ }
+ return mPermissions.get(name);
+ }
+ }
+
+ /**
+ * Gets all permission states.
+ *
+ * @return The permission states or an empty set.
+ */
+ @NonNull
+ public List<PermissionState> getPermissionStates() {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return Collections.emptyList();
+ }
+ return new ArrayList<>(mPermissions.values());
+ }
+ }
+
+ /**
+ * Put a permission state.
+ */
+ public void putPermissionState(@NonNull BasePermission permission, boolean isRuntime,
+ boolean isGranted, int flags) {
+ synchronized (mLock) {
+ ensureNoPermissionState(permission.name);
+ PermissionState permissionState = ensurePermissionState(permission, isRuntime);
+ if (isGranted) {
+ permissionState.grant();
+ }
+ permissionState.updateFlags(flags, flags);
+ if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ mPermissionReviewRequired = true;
+ }
+ }
+ }
+
+ /**
+ * Grant a permission.
+ *
+ * @param permission The permission to grant.
+ * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+ * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
+ * #PERMISSION_OPERATION_FAILURE}.
+ */
+ public int grantPermission(@NonNull BasePermission permission) {
+ if (hasPermission(permission.getName())) {
+ return PERMISSION_OPERATION_SUCCESS;
+ }
+
+ PermissionState permissionState = ensurePermissionState(permission);
+
+ if (!permissionState.grant()) {
+ return PERMISSION_OPERATION_FAILURE;
+ }
+
+ return permission.hasGids() ? PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED
+ : PERMISSION_OPERATION_SUCCESS;
+ }
+
+ /**
+ * Revoke a permission.
+ *
+ * @param permission The permission to revoke.
+ * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+ * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
+ * #PERMISSION_OPERATION_FAILURE}.
+ */
+ public int revokePermission(@NonNull BasePermission permission) {
+ final String permissionName = permission.getName();
+ if (!hasPermission(permissionName)) {
+ return PERMISSION_OPERATION_SUCCESS;
+ }
+
+ PermissionState permissionState;
+ synchronized (mLock) {
+ permissionState = mPermissions.get(permissionName);
+ }
+
+ if (!permissionState.revoke()) {
+ return PERMISSION_OPERATION_FAILURE;
+ }
+
+ if (permissionState.isDefault()) {
+ ensureNoPermissionState(permissionName);
+ }
+
+ return permission.hasGids() ? PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED
+ : PERMISSION_OPERATION_SUCCESS;
+ }
+
+ // TODO: fix this to use arraycopy and append all ints in one go
+ private static int[] appendInts(int[] current, int[] added) {
+ if (current != null && added != null) {
+ for (int guid : added) {
+ current = ArrayUtils.appendInt(current, guid);
+ }
+ }
+ return current;
+ }
+
+ @NonNull
+ private PermissionState ensurePermissionState(@NonNull BasePermission permission) {
+ return ensurePermissionState(permission, permission.isRuntime());
+ }
+
+ @NonNull
+ private PermissionState ensurePermissionState(@NonNull BasePermission permission,
+ boolean isRuntime) {
+ final String permissionName = permission.getName();
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ mPermissions = new ArrayMap<>();
+ }
+ PermissionState permissionState = mPermissions.get(permissionName);
+ if (permissionState == null) {
+ permissionState = new PermissionState(permission, isRuntime);
+ mPermissions.put(permissionName, permissionState);
+ }
+ return permissionState;
+ }
+ }
+
+ private void ensureNoPermissionState(@NonNull String name) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return;
+ }
+ mPermissions.remove(name);
+ if (mPermissions.isEmpty()) {
+ mPermissions = null;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/UserPermissionState.java b/services/core/java/com/android/server/pm/permission/UserPermissionState.java
new file mode 100644
index 0000000..7f55cb1
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/UserPermissionState.java
@@ -0,0 +1,103 @@
+/*
+ * 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.permission;
+
+import android.annotation.AppIdInt;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * Permission state for a user.
+ */
+public final class UserPermissionState {
+ /**
+ * Whether the install permissions have been granted to a package, so that no install
+ * permissions should be added to it unless the package is upgraded.
+ */
+ @GuardedBy("mLock")
+ @NonNull
+ private final ArraySet<String> mInstallPermissionsFixed = new ArraySet<>();
+
+ /**
+ * Maps from app ID to {@link UidPermissionState}.
+ */
+ @GuardedBy("mLock")
+ @NonNull
+ private final SparseArray<UidPermissionState> mUidStates = new SparseArray<>();
+
+ @NonNull
+ private final Object mLock;
+
+ public UserPermissionState(@NonNull Object lock) {
+ mLock = lock;
+ }
+
+ public boolean areInstallPermissionsFixed(@NonNull String packageName) {
+ synchronized (mLock) {
+ return mInstallPermissionsFixed.contains(packageName);
+ }
+ }
+
+ public void setInstallPermissionsFixed(@NonNull String packageName, boolean fixed) {
+ synchronized (mLock) {
+ if (fixed) {
+ mInstallPermissionsFixed.add(packageName);
+ } else {
+ mInstallPermissionsFixed.remove(packageName);
+ }
+ }
+ }
+
+ @Nullable
+ public UidPermissionState getUidState(@AppIdInt int appId) {
+ checkAppId(appId);
+ synchronized (mLock) {
+ return mUidStates.get(appId);
+ }
+ }
+
+ @NonNull
+ public UidPermissionState getOrCreateUidState(@AppIdInt int appId) {
+ checkAppId(appId);
+ synchronized (mLock) {
+ UidPermissionState uidState = mUidStates.get(appId);
+ if (uidState == null) {
+ uidState = new UidPermissionState();
+ mUidStates.put(appId, uidState);
+ }
+ return uidState;
+ }
+ }
+
+ public void removeUidState(@AppIdInt int appId) {
+ checkAppId(appId);
+ synchronized (mLock) {
+ mUidStates.delete(appId);
+ }
+ }
+
+ private void checkAppId(@AppIdInt int appId) {
+ if (UserHandle.getUserId(appId) != 0) {
+ throw new IllegalArgumentException(appId + " is not an app ID");
+ }
+ }
+}
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 2ca849d..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;
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/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/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3a2b670..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);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f8d457e..183a149 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2081,9 +2081,7 @@
ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who, int userHandle, boolean parent) {
ensureLocked();
if (parent) {
- Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format(
- "You can not call APIs on the parent profile outside a managed profile, "
- + "userId = %d", userHandle));
+ enforceManagedProfile(userHandle, "call APIs on the parent profile");
}
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
if (admin != null && parent) {
@@ -2259,7 +2257,8 @@
@Nullable String permission) throws SecurityException {
ensureLocked();
if (parent) {
- Preconditions.checkCallingUser(isManagedProfile(getCallerIdentity(who).getUserId()));
+ enforceManagedProfile(mInjector.userHandleGetCallingUserId(),
+ "call APIs on the parent profile");
}
ActiveAdmin admin = getActiveAdminOrCheckPermissionForCallerLocked(
who, reqPolicy, permission);
@@ -2853,7 +2852,7 @@
if (!mHasFeature) {
return;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+ enforceManageUsers();
synchronized (getLockObject()) {
final DevicePolicyData policy = getUserData(userHandle.getIdentifier());
@@ -4043,13 +4042,10 @@
if (!mHasFeature) {
return true;
}
- Objects.requireNonNull(admin, "ComponentName is null");
-
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
- Preconditions.checkCallingUser(isManagedProfile(caller.getUserId()));
-
- return !isSeparateProfileChallengeEnabled(caller.getUserId());
+ final int userId = mInjector.userHandleGetCallingUserId();
+ enforceProfileOrDeviceOwner(admin);
+ enforceManagedProfile(userId, "query unified challenge status");
+ return !isSeparateProfileChallengeEnabled(userId);
}
@Override
@@ -4061,9 +4057,7 @@
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
- Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format(
- "can not call APIs refering to the parent profile outside a managed profile, "
- + "userId = %d", userHandle));
+ enforceManagedProfile(userHandle, "call APIs refering to the parent profile");
synchronized (getLockObject()) {
final int targetUser = getProfileParentId(userHandle);
@@ -4085,9 +4079,7 @@
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
- Preconditions.checkCallAuthorization(!isManagedProfile(userHandle), String.format(
- "You can not check password sufficiency for a managed profile, userId = %d",
- userHandle));
+ enforceNotManagedProfile(userHandle, "check password sufficiency");
enforceUserUnlocked(userHandle);
synchronized (getLockObject()) {
@@ -4643,9 +4635,8 @@
if (!mHasFeature && !hasCallingPermission(permission.LOCK_DEVICE)) {
return;
}
- final CallerIdentity caller = getCallerIdentity();
- final int callingUserId = caller.getUserId();
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
ComponentName adminComponent = null;
synchronized (getLockObject()) {
// Make sure the caller has any active admin with the right policy or
@@ -4662,13 +4653,16 @@
// For Profile Owners only, callers with only permission not allowed.
if ((flags & DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY) != 0) {
// Evict key
- Preconditions.checkCallingUser(isManagedProfile(callingUserId));
- Preconditions.checkArgument(!parent,
- "Cannot set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY for the parent");
+ enforceManagedProfile(
+ callingUserId, "set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY");
if (!isProfileOwner(adminComponent, callingUserId)) {
throw new SecurityException("Only profile owner admins can set "
+ "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY");
}
+ if (parent) {
+ throw new IllegalArgumentException(
+ "Cannot set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY for the parent");
+ }
if (!mInjector.storageManagerIsFileBasedEncryptionEnabled()) {
throw new UnsupportedOperationException(
"FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY only applies to FBE devices");
@@ -4713,19 +4707,32 @@
@Override
public void enforceCanManageCaCerts(ComponentName who, String callerPackage) {
- final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(canManageCaCerts(caller));
+ if (who == null) {
+ if (!isCallerDelegate(callerPackage, mInjector.binderGetCallingUid(),
+ DELEGATION_CERT_INSTALL)) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null);
+ }
+ } else {
+ enforceProfileOrDeviceOwner(who);
+ }
}
- private boolean canManageCaCerts(CallerIdentity caller) {
- return isDeviceOwner(caller) || isProfileOwner(caller) || isCallerDelegate(caller,
- DELEGATION_CERT_INSTALL) || hasCallingOrSelfPermission(MANAGE_CA_CERTIFICATES);
+ private void enforceProfileOrDeviceOwner(ComponentName who) {
+ synchronized (getLockObject()) {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ }
+ }
+
+ private void enforceNetworkStackOrProfileOrDeviceOwner(ComponentName who) {
+ if (hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK)) {
+ return;
+ }
+ enforceProfileOrDeviceOwner(who);
}
@Override
public boolean approveCaCert(String alias, int userId, boolean approval) {
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
synchronized (getLockObject()) {
Set<String> certs = getUserData(userId).mAcceptedCaCertificates;
boolean changed = (approval ? certs.add(alias) : certs.remove(alias));
@@ -4740,8 +4747,7 @@
@Override
public boolean isCaCertApproved(String alias, int userId) {
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
synchronized (getLockObject()) {
return getUserData(userId).mAcceptedCaCertificates.contains(alias);
}
@@ -4766,20 +4772,21 @@
}
@Override
- public boolean installCaCert(ComponentName admin, String callerPackage, byte[] certBuffer) {
+ public boolean installCaCert(ComponentName admin, String callerPackage, byte[] certBuffer)
+ throws RemoteException {
if (!mHasFeature) {
return false;
}
- final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
- Preconditions.checkCallAuthorization(canManageCaCerts(caller));
+ enforceCanManageCaCerts(admin, callerPackage);
+ final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
final String alias = mInjector.binderWithCleanCallingIdentity(() -> {
- String installedAlias = mCertificateMonitor.installCaCert(
- caller.getUserHandle(), certBuffer);
+ String installedAlias = mCertificateMonitor.installCaCert(userHandle, certBuffer);
+ final boolean isDelegate = (admin == null);
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.INSTALL_CA_CERT)
- .setAdmin(caller.getPackageName())
- .setBoolean(/* isDelegate */ admin == null)
+ .setAdmin(callerPackage)
+ .setBoolean(isDelegate)
.write();
return installedAlias;
});
@@ -4790,8 +4797,8 @@
}
synchronized (getLockObject()) {
- getUserData(caller.getUserId()).mOwnerInstalledCaCerts.add(alias);
- saveSettingsLocked(caller.getUserId());
+ getUserData(userHandle.getIdentifier()).mOwnerInstalledCaCerts.add(alias);
+ saveSettingsLocked(userHandle.getIdentifier());
}
return true;
}
@@ -4801,22 +4808,22 @@
if (!mHasFeature) {
return;
}
- final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
- Preconditions.checkCallAuthorization(canManageCaCerts(caller));
+ enforceCanManageCaCerts(admin, callerPackage);
+ final int userId = mInjector.userHandleGetCallingUserId();
mInjector.binderWithCleanCallingIdentity(() -> {
- mCertificateMonitor.uninstallCaCerts(caller.getUserHandle(), aliases);
+ mCertificateMonitor.uninstallCaCerts(UserHandle.of(userId), aliases);
+ final boolean isDelegate = (admin == null);
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.UNINSTALL_CA_CERTS)
- .setAdmin(caller.getPackageName())
- .setBoolean(/* isDelegate */ admin == null)
+ .setAdmin(callerPackage)
+ .setBoolean(isDelegate)
.write();
});
synchronized (getLockObject()) {
- if (getUserData(caller.getUserId()).mOwnerInstalledCaCerts.removeAll(
- Arrays.asList(aliases))) {
- saveSettingsLocked(caller.getUserId());
+ if (getUserData(userId).mOwnerInstalledCaCerts.removeAll(Arrays.asList(aliases))) {
+ saveSettingsLocked(userId);
}
}
}
@@ -5606,10 +5613,8 @@
public boolean setAlwaysOnVpnPackage(ComponentName who, String vpnPackage, boolean lockdown,
List<String> lockdownWhitelist)
throws SecurityException {
- Objects.requireNonNull(who, "ComponentName is null");
-
+ enforceProfileOrDeviceOwner(who);
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
final int userId = caller.getUserId();
mInjector.binderWithCleanCallingIdentity(() -> {
@@ -5635,7 +5640,7 @@
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_ALWAYS_ON_VPN_PACKAGE)
- .setAdmin(caller.getComponentName())
+ .setAdmin(who)
.setStrings(vpnPackage)
.setBoolean(lockdown)
.setInt(lockdownWhitelist != null ? lockdownWhitelist.size() : 0)
@@ -5655,14 +5660,11 @@
@Override
public String getAlwaysOnVpnPackage(ComponentName admin) throws SecurityException {
- Objects.requireNonNull(admin, "ComponentName is null");
+ enforceProfileOrDeviceOwner(admin);
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
-
+ final int userId = mInjector.userHandleGetCallingUserId();
return mInjector.binderWithCleanCallingIdentity(
- () -> mInjector.getConnectivityManager().getAlwaysOnVpnPackageForUser(
- caller.getUserId()));
+ () -> mInjector.getConnectivityManager().getAlwaysOnVpnPackageForUser(userId));
}
@Override
@@ -5676,14 +5678,11 @@
@Override
public boolean isAlwaysOnVpnLockdownEnabled(ComponentName admin) throws SecurityException {
- Objects.requireNonNull(admin, "ComponentName is null");
+ enforceNetworkStackOrProfileOrDeviceOwner(admin);
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK));
-
+ final int userId = mInjector.userHandleGetCallingUserId();
return mInjector.binderWithCleanCallingIdentity(
- () -> mInjector.getConnectivityManager().isVpnLockdownEnabled(caller.getUserId()));
+ () -> mInjector.getConnectivityManager().isVpnLockdownEnabled(userId));
}
@Override
@@ -5698,14 +5697,11 @@
@Override
public List<String> getAlwaysOnVpnLockdownWhitelist(ComponentName admin)
throws SecurityException {
- Objects.requireNonNull(admin, "ComponentName is null");
+ enforceProfileOrDeviceOwner(admin);
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
-
+ final int userId = mInjector.userHandleGetCallingUserId();
return mInjector.binderWithCleanCallingIdentity(
- () -> mInjector.getConnectivityManager().getVpnLockdownWhitelist(
- caller.getUserId()));
+ () -> mInjector.getConnectivityManager().getVpnLockdownWhitelist(userId));
}
private void forceWipeDeviceNoLock(boolean wipeExtRequested, String reason, boolean wipeEuicc) {
@@ -5994,13 +5990,11 @@
if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
+ enforceSystemCaller("report password change");
- final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isSystemUid(caller));
// Managed Profile password can only be changed when it has a separate challenge.
if (!isSeparateProfileChallengeEnabled(userId)) {
- Preconditions.checkCallAuthorization(!isManagedProfile(userId), String.format("You can "
- + "not set the active password for a managed profile, userId = %d", userId));
+ enforceNotManagedProfile(userId, "set the active password");
}
DevicePolicyData policy = getUserData(userId);
@@ -6053,9 +6047,8 @@
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
if (!isSeparateProfileChallengeEnabled(userHandle)) {
- Preconditions.checkCallAuthorization(!isManagedProfile(userHandle), String.format(
- "You can not report failed password attempt if separate profile challenge is "
- + "not in place for a managed profile, userId = %d", userHandle));
+ enforceNotManagedProfile(userHandle,
+ "report failed password attempt if separate profile challenge is not in place");
}
boolean wipeData = false;
@@ -7284,7 +7277,7 @@
return null;
}
if (!callingUserOnly) {
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+ enforceManageUsers();
}
synchronized (getLockObject()) {
if (!mOwners.hasDeviceOwner()) {
@@ -7303,8 +7296,7 @@
if (!mHasFeature) {
return UserHandle.USER_NULL;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
synchronized (getLockObject()) {
return mOwners.hasDeviceOwner() ? mOwners.getDeviceOwnerUserId() : UserHandle.USER_NULL;
}
@@ -7319,8 +7311,7 @@
if (!mHasFeature) {
return null;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
synchronized (getLockObject()) {
if (!mOwners.hasDeviceOwner()) {
return null;
@@ -7545,10 +7536,8 @@
}
Objects.requireNonNull(who, "ComponentName is null");
- final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallingUser(!isManagedProfile(caller.getUserId()));
-
- final int userId = caller.getUserId();
+ final int userId = mInjector.userHandleGetCallingUserId();
+ enforceNotManagedProfile(userId, "clear profile owner");
enforceUserUnlocked(userId);
synchronized (getLockObject()) {
// Check if this is the profile owner who is calling
@@ -7665,10 +7654,9 @@
if (!mHasFeature) {
return DevicePolicyManager.STATE_USER_UNMANAGED;
}
- final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(canManageUsers(caller));
-
- return getUserProvisioningState(caller.getUserId());
+ enforceManageUsers();
+ int userHandle = mInjector.userHandleGetCallingUserId();
+ return getUserProvisioningState(userHandle);
}
private int getUserProvisioningState(int userHandle) {
@@ -7706,8 +7694,8 @@
}
transitionCheckNeeded = false;
} else {
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+ // For all other cases, caller must have MANAGE_PROFILE_AND_DEVICE_OWNERS.
+ enforceCanManageProfileAndDeviceOwners();
}
final DevicePolicyData policyData = getUserData(userHandle);
@@ -7762,13 +7750,11 @@
return;
}
Objects.requireNonNull(who, "ComponentName is null");
-
- final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller));
- Preconditions.checkCallingUser(isManagedProfile(caller.getUserId()));
-
synchronized (getLockObject()) {
- final int userId = caller.getUserId();
+ // Check if this is the profile owner who is calling
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ final int userId = UserHandle.getCallingUserId();
+ enforceManagedProfile(userId, "enable the profile");
// Check if the profile is already enabled.
UserInfo managedProfile = getUserInfo(userId);
if (managedProfile.isEnabled()) {
@@ -7794,15 +7780,14 @@
@Override
public void setProfileName(ComponentName who, String profileName) {
Objects.requireNonNull(who, "ComponentName is null");
+ enforceProfileOrDeviceOwner(who);
- final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
-
+ final int userId = UserHandle.getCallingUserId();
mInjector.binderWithCleanCallingIdentity(() -> {
- mUserManager.setUserName(caller.getUserId(), profileName);
+ mUserManager.setUserName(userId, profileName);
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_PROFILE_NAME)
- .setAdmin(caller.getComponentName())
+ .setAdmin(who)
.write();
});
}
@@ -7911,8 +7896,7 @@
if (!mHasFeature) {
return null;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
ComponentName profileOwner = getProfileOwner(userHandle);
if (profileOwner == null) {
return null;
@@ -8084,8 +8068,7 @@
}
return;
}
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+ enforceCanManageProfileAndDeviceOwners();
if ((mIsWatch || hasUserSetupCompleted(userHandle))) {
if (!isCallerWithSystemUid()) {
@@ -8121,8 +8104,7 @@
@UserIdInt int userId,
boolean hasIncompatibleAccountsOrNonAdb) {
if (!isAdb()) {
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+ enforceCanManageProfileAndDeviceOwners();
}
final int code = checkDeviceOwnerProvisioningPreConditionLocked(
@@ -8176,9 +8158,11 @@
}
}
- private boolean canManageUsers(CallerIdentity caller) {
- return isSystemUid(caller) || isRootUid(caller)
- || hasCallingOrSelfPermission(permission.MANAGE_USERS);
+ private void enforceManageUsers() {
+ final int callingUid = mInjector.binderGetCallingUid();
+ if (!(isCallerWithSystemUid() || callingUid == Process.ROOT_UID)) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
+ }
}
private boolean hasCallingPermission(String permission) {
@@ -8208,6 +8192,20 @@
|| hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS);
}
+ private void enforceManagedProfile(int userId, String message) {
+ if (!isManagedProfile(userId)) {
+ throw new SecurityException(String.format(
+ "You can not %s outside a managed profile, userId = %d", message, userId));
+ }
+ }
+
+ private void enforceNotManagedProfile(int userId, String message) {
+ if (isManagedProfile(userId)) {
+ throw new SecurityException(String.format(
+ "You can not %s for a managed profile, userId = %d", message, userId));
+ }
+ }
+
private void enforceDeviceOwnerOrManageUsers() {
synchronized (getLockObject()) {
if (getActiveAdminWithPolicyForUidLocked(null, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER,
@@ -8215,7 +8213,7 @@
return;
}
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+ enforceManageUsers();
}
private void enforceProfileOwnerOrSystemUser() {
@@ -8816,7 +8814,6 @@
return null;
}
Objects.requireNonNull(who, "ComponentName is null");
-
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
@@ -8831,8 +8828,7 @@
if (!mHasFeature) {
return null;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
synchronized (getLockObject()) {
List<String> result = null;
// If we have multiple profiles we return the intersection of the
@@ -8917,7 +8913,6 @@
return false;
}
Objects.requireNonNull(who, "ComponentName is null");
-
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
@@ -8959,7 +8954,6 @@
return null;
}
Objects.requireNonNull(who, "ComponentName is null");
-
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
@@ -8971,13 +8965,13 @@
@Override
public List getPermittedInputMethodsForCurrentUser() {
- final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(canManageUsers(caller));
+ enforceManageUsers();
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
synchronized (getLockObject()) {
List<String> result = null;
// Only device or profile owners can have permitted lists set.
- DevicePolicyData policy = getUserDataUnchecked(caller.getUserId());
+ DevicePolicyData policy = getUserDataUnchecked(callingUserId);
for (int i = 0; i < policy.mAdminList.size(); i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
List<String> fromAdmin = admin.permittedInputMethods;
@@ -8992,8 +8986,8 @@
// If we have a permitted list add all system input methods.
if (result != null) {
- List<InputMethodInfo> imes = InputMethodManagerInternal
- .get().getInputMethodListAsUser(caller.getUserId());
+ List<InputMethodInfo> imes =
+ InputMethodManagerInternal.get().getInputMethodListAsUser(callingUserId);
if (imes != null) {
for (InputMethodInfo ime : imes) {
ServiceInfo serviceInfo = ime.getServiceInfo();
@@ -9437,12 +9431,11 @@
@Override
public boolean isEphemeralUser(ComponentName who) {
Objects.requireNonNull(who, "ComponentName is null");
+ enforceProfileOrDeviceOwner(who);
- final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
-
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
return mInjector.binderWithCleanCallingIdentity(
- () -> mInjector.getUserManager().isUserEphemeral(caller.getUserId()));
+ () -> mInjector.getUserManager().isUserEphemeral(callingUserId));
}
@Override
@@ -10170,7 +10163,6 @@
return;
}
Objects.requireNonNull(who, "ComponentName is null");
-
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
@@ -10194,7 +10186,6 @@
return false;
}
Objects.requireNonNull(who, "ComponentName is null");
-
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
@@ -10217,23 +10208,12 @@
@Override
public void setSecondaryLockscreenEnabled(ComponentName who, boolean enabled) {
- Objects.requireNonNull(who, "ComponentName is null");
-
- // Check can set secondary lockscreen enabled
- final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
- Preconditions.checkCallAuthorization(!isManagedProfile(caller.getUserId()),
- String.format("User %d is not allowed to call setSecondaryLockscreenEnabled",
- caller.getUserId()));
- // Allow testOnly admins to bypass supervision config requirement.
- Preconditions.checkCallAuthorization(isAdminTestOnlyLocked(who, caller.getUserId())
- || isDefaultSupervisor(caller), String.format("Admin %s is not the "
- + "default supervision component", caller.getComponentName()));
-
+ enforceCanSetSecondaryLockscreenEnabled(who);
synchronized (getLockObject()) {
- DevicePolicyData policy = getUserData(caller.getUserId());
+ final int userId = mInjector.userHandleGetCallingUserId();
+ DevicePolicyData policy = getUserData(userId);
policy.mSecondaryLockscreenEnabled = enabled;
- saveSettingsLocked(caller.getUserId());
+ saveSettingsLocked(userId);
}
}
@@ -10244,14 +10224,31 @@
}
}
- private boolean isDefaultSupervisor(CallerIdentity caller) {
+ private void enforceCanSetSecondaryLockscreenEnabled(ComponentName who) {
+ enforceProfileOrDeviceOwner(who);
+ final int userId = mInjector.userHandleGetCallingUserId();
+ if (isManagedProfile(userId)) {
+ 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);
if (supervisor == null) {
- return false;
+ throw new SecurityException("Unable to set secondary lockscreen setting, no "
+ + "default supervision component defined");
}
final ComponentName supervisorComponent = ComponentName.unflattenFromString(supervisor);
- return caller.getComponentName().equals(supervisorComponent);
+ if (!who.equals(supervisorComponent)) {
+ throw new SecurityException(
+ "Admin " + who + " is not the default supervision component");
+ }
}
@Override
@@ -11591,9 +11588,7 @@
@Override
public SystemUpdateInfo getPendingSystemUpdate(ComponentName admin) {
Objects.requireNonNull(admin, "ComponentName is null");
-
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ enforceProfileOrDeviceOwner(admin);
return mOwners.getSystemUpdateInfo();
}
@@ -11784,11 +11779,8 @@
@Override
public int checkProvisioningPreCondition(String action, String packageName) {
- Objects.requireNonNull(packageName, "packageName is null");
-
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
-
+ Objects.requireNonNull(packageName);
+ enforceCanManageProfileAndDeviceOwners();
return checkProvisioningPreConditionSkipPermission(action, packageName);
}
@@ -12049,12 +12041,8 @@
@Override
public boolean isManagedProfile(ComponentName admin) {
- Objects.requireNonNull(admin, "ComponentName is null");
-
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
-
- return isManagedProfile(caller.getUserId());
+ enforceProfileOrDeviceOwner(admin);
+ return isManagedProfile(mInjector.userHandleGetCallingUserId());
}
@Override
@@ -12180,10 +12168,8 @@
return;
}
Objects.requireNonNull(who, "ComponentName is null");
-
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallingUser(isManagedProfile(caller.getUserId()));
-
+ enforceManagedProfile(caller.getUserId(), "set organization color");
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
admin.organizationColor = color;
@@ -12191,7 +12177,7 @@
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_ORGANIZATION_COLOR)
- .setAdmin(caller.getComponentName())
+ .setAdmin(who)
.write();
}
@@ -12204,10 +12190,9 @@
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
- Preconditions.checkCallAuthorization(canManageUsers(caller));
- Preconditions.checkCallAuthorization(isManagedProfile(userId), String.format("You can not "
- + "set organization color outside a managed profile, userId = %d", userId));
+ enforceManageUsers();
+ enforceManagedProfile(userId, "set organization color");
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
admin.organizationColor = color;
@@ -12221,10 +12206,8 @@
return ActiveAdmin.DEF_ORGANIZATION_COLOR;
}
Objects.requireNonNull(who, "ComponentName is null");
-
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallingUser(isManagedProfile(caller.getUserId()));
-
+ enforceManagedProfile(caller.getUserId(), "get organization color");
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
return admin.organizationColor;
@@ -12240,9 +12223,8 @@
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
- Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format("You can "
- + "not get organization color outside a managed profile, userId = %d", userHandle));
+ enforceManagedProfile(userHandle, "get organization color");
synchronized (getLockObject()) {
ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userHandle);
return (profileOwner != null)
@@ -12275,10 +12257,8 @@
return null;
}
Objects.requireNonNull(who, "ComponentName is null");
-
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallingUser(isManagedProfile(caller.getUserId()));
-
+ enforceManagedProfile(caller.getUserId(), "get organization name");
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
return admin.organizationName;
@@ -12306,10 +12286,8 @@
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
- Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format(
- "You can not get organization name outside a managed profile, userId = %d",
- userHandle));
+ enforceManagedProfile(userHandle, "get organization name");
synchronized (getLockObject()) {
ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userHandle);
return (profileOwner != null)
@@ -12744,6 +12722,16 @@
return mSecurityLogMonitor.forceLogs();
}
+ private void enforceCanManageDeviceAdmin() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS,
+ null);
+ }
+
+ private void enforceCanManageProfileAndDeviceOwners() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
+ }
+
private void enforceCallerSystemUserHandle() {
final int callingUid = mInjector.binderGetCallingUid();
final int userId = UserHandle.getUserId(callingUid);
@@ -12754,11 +12742,9 @@
@Override
public boolean isUninstallInQueue(final String packageName) {
- final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
-
- Pair<String, Integer> packageUserPair = new Pair<>(packageName, caller.getUserId());
+ enforceCanManageDeviceAdmin();
+ final int userId = mInjector.userHandleGetCallingUserId();
+ Pair<String, Integer> packageUserPair = new Pair<>(packageName, userId);
synchronized (getLockObject()) {
return mPackagesToRemove.contains(packageUserPair);
}
@@ -12766,13 +12752,11 @@
@Override
public void uninstallPackageWithActiveAdmins(final String packageName) {
+ enforceCanManageDeviceAdmin();
Preconditions.checkArgument(!TextUtils.isEmpty(packageName));
- final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
+ final int userId = mInjector.userHandleGetCallingUserId();
- final int userId = caller.getUserId();
enforceUserUnlocked(userId);
final ComponentName profileOwner = getProfileOwner(userId);
@@ -12821,9 +12805,7 @@
@Override
public boolean isDeviceProvisioned() {
- final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(canManageUsers(caller));
-
+ enforceManageUsers();
synchronized (getLockObject()) {
return getUserDataUnchecked(UserHandle.USER_SYSTEM).mUserSetupComplete;
}
@@ -12907,8 +12889,7 @@
@Override
public void setDeviceProvisioningConfigApplied() {
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
synchronized (getLockObject()) {
DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
policy.mDeviceProvisioningConfigApplied = true;
@@ -12918,8 +12899,7 @@
@Override
public boolean isDeviceProvisioningConfigApplied() {
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
synchronized (getLockObject()) {
final DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
return policy.mDeviceProvisioningConfigApplied;
@@ -12935,10 +12915,8 @@
*/
@Override
public void forceUpdateUserSetupComplete() {
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+ enforceCanManageProfileAndDeviceOwners();
enforceCallerSystemUserHandle();
-
// no effect if it's called from user build
if (!mInjector.isBuildDebuggable()) {
return;
@@ -12959,28 +12937,25 @@
if (!mHasFeature) {
return;
}
- Objects.requireNonNull(admin, "ComponentName is null");
-
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
-
- toggleBackupServiceActive(caller.getUserId(), enabled);
+ Objects.requireNonNull(admin);
+ enforceProfileOrDeviceOwner(admin);
+ int userId = mInjector.userHandleGetCallingUserId();
+ toggleBackupServiceActive(userId, enabled);
}
@Override
public boolean isBackupServiceEnabled(ComponentName admin) {
+ Objects.requireNonNull(admin);
if (!mHasFeature) {
return true;
}
- Objects.requireNonNull(admin, "ComponentName is null");
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
-
+ enforceProfileOrDeviceOwner(admin);
synchronized (getLockObject()) {
try {
IBackupManager ibm = mInjector.getIBackupManager();
- return ibm != null && ibm.isBackupServiceActive(caller.getUserId());
+ return ibm != null && ibm.isBackupServiceActive(
+ mInjector.userHandleGetCallingUserId());
} catch (RemoteException e) {
throw new IllegalStateException("Failed requesting backup service state.", e);
}
@@ -13292,9 +13267,9 @@
return false;
}
final CallerIdentity caller = getCallerIdentity(admin, packageName);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING)
- || hasCallingOrSelfPermission(permission.MANAGE_USERS));
+ Preconditions.checkCallAuthorization(
+ isDeviceOwner(caller) || isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING)
+ || hasCallingOrSelfPermission(permission.MANAGE_USERS));
synchronized (getLockObject()) {
return isNetworkLoggingEnabledInternalLocked();
@@ -13562,14 +13537,13 @@
Objects.requireNonNull(admin, "ComponentName is null");
Objects.requireNonNull(packageName, "packageName is null");
Objects.requireNonNull(callback, "callback is null");
-
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ enforceProfileOrDeviceOwner(admin);
+ final int userId = UserHandle.getCallingUserId();
long ident = mInjector.binderClearCallingIdentity();
try {
ActivityManager.getService().clearApplicationUserData(packageName, false, callback,
- caller.getUserId());
+ userId);
} catch(RemoteException re) {
// Same process, should not happen.
} catch (SecurityException se) {
@@ -13622,9 +13596,7 @@
@Override
public List<String> getDisallowedSystemApps(ComponentName admin, int userId,
String provisioningAction) throws RemoteException {
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
-
+ enforceCanManageProfileAndDeviceOwners();
return new ArrayList<>(
mOverlayPackagesProvider.getNonRequiredApps(admin, userId, provisioningAction));
}
@@ -13635,23 +13607,31 @@
if (!mHasFeature) {
return;
}
- Objects.requireNonNull(admin, "ComponentName is null");
+
+ Objects.requireNonNull(admin, "Admin cannot be null.");
Objects.requireNonNull(target, "Target cannot be null.");
- Preconditions.checkArgument(!admin.equals(target),
- "Provided administrator and target are the same object.");
- Preconditions.checkArgument(!admin.getPackageName().equals(target.getPackageName()),
- "Provided administrator and target have the same package name.");
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ enforceProfileOrDeviceOwner(admin);
- final int callingUserId = caller.getUserId();
+ if (admin.equals(target)) {
+ throw new IllegalArgumentException("Provided administrator and target are "
+ + "the same object.");
+ }
+
+ if (admin.getPackageName().equals(target.getPackageName())) {
+ throw new IllegalArgumentException("Provided administrator and target have "
+ + "the same package name.");
+ }
+
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
final DevicePolicyData policy = getUserData(callingUserId);
final DeviceAdminInfo incomingDeviceInfo = findAdmin(target, callingUserId,
/* throwForMissingPermission= */ true);
checkActiveAdminPrecondition(target, incomingDeviceInfo, policy);
- Preconditions.checkArgument(incomingDeviceInfo.supportsTransferOwnership(),
- "Provided target does not support ownership transfer.");
+ if (!incomingDeviceInfo.supportsTransferOwnership()) {
+ throw new IllegalArgumentException("Provided target does not support "
+ + "ownership transfer.");
+ }
final long id = mInjector.binderClearCallingIdentity();
String ownerType = null;
@@ -13674,7 +13654,7 @@
if (bundle == null) {
bundle = new PersistableBundle();
}
- if (isProfileOwner(caller)) {
+ if (isProfileOwner(admin, callingUserId)) {
ownerType = ADMIN_TYPE_PROFILE_OWNER;
prepareTransfer(admin, target, bundle, callingUserId,
ADMIN_TYPE_PROFILE_OWNER);
@@ -13685,7 +13665,7 @@
if (isUserAffiliatedWithDeviceLocked(callingUserId)) {
notifyAffiliatedProfileTransferOwnershipComplete(callingUserId);
}
- } else if (isDeviceOwner(caller)) {
+ } else if (isDeviceOwner(admin, callingUserId)) {
ownerType = ADMIN_TYPE_DEVICE_OWNER;
prepareTransfer(admin, target, bundle, callingUserId,
ADMIN_TYPE_DEVICE_OWNER);
@@ -14355,8 +14335,7 @@
if (!mHasFeature) {
return false;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
long id = mInjector.binderClearCallingIdentity();
try {
return isManagedKioskInternal();
@@ -14381,8 +14360,7 @@
if (!mHasFeature) {
return false;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
return mInjector.binderWithCleanCallingIdentity(() -> isUnattendedManagedKioskUnchecked());
}
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/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 1cf133a..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,6 +300,7 @@
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.
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 631b4d4..4ce6411 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -4407,7 +4407,7 @@
// Caller is Profile Owner, but no supervision app is configured.
setAsProfileOwner(admin1);
- assertExpectException(SecurityException.class, "is not the default supervision component",
+ assertExpectException(SecurityException.class, "no default supervision component defined",
() -> dpm.setSecondaryLockscreenEnabled(admin1, true));
assertFalse(dpm.isSecondaryLockscreenEnabled(UserHandle.of(CALLER_USER_HANDLE)));
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/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/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/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/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/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/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/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);
+ }
+ }
+
}