diff options
117 files changed, 5462 insertions, 4240 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java index 42725c51fd87..bf5781beb52b 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 @@ public abstract class JobScheduler { * 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 @@ public abstract class JobScheduler { * 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/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index cf4caea9c487..b638fef36a9e 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.AppStateTrackerImpl; 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 000000000000..b47a210cbed8 --- /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 d7be2595e88b..c7cc2f03b062 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.JobInfo; 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 final class JobStatus { public ArraySet<Uri> changedUris; public ArraySet<String> changedAuthorities; public Network network; + public ServiceInfo serviceInfo; public int lastEvaluatedPriority; @@ -1284,8 +1286,8 @@ public final class JobStatus { // 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 @@ public final class JobStatus { 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 592a909f7e14..2b662eb0676d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -46766,6 +46766,7 @@ package android.telephony { 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"; @@ -46993,6 +46994,8 @@ package android.telephony { 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 60145a8242f6..65fe85bdd727 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1592,12 +1592,16 @@ package android.bluetooth { 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 { @@ -9374,6 +9378,7 @@ package android.provider { 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"; diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index fc1455be29b1..0a09801708a5 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -38,6 +38,7 @@ import "frameworks/base/core/proto/android/net/networkcapabilities.proto"; 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 @@ message Atom { 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 @@ message BlobInfo { // 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 4139b2faeb1b..b0307d1eb939 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 73c38cbfbe3f..ce3c7d25a108 100644 --- a/core/java/android/bluetooth/BluetoothPan.java +++ b/core/java/android/bluetooth/BluetoothPan.java @@ -89,6 +89,33 @@ public final class BluetoothPan implements BluetoothProfile { @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 final class BluetoothPan implements BluetoothProfile { 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 6673cb933eff..d5f2c12e8462 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -146,8 +146,15 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 0cdad9f8de93..400b312bb913 100644 --- a/core/java/android/preference/SeekBarVolumizer.java +++ b/core/java/android/preference/SeekBarVolumizer.java @@ -46,6 +46,8 @@ import android.widget.SeekBar.OnSeekBarChangeListener; 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 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba 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 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba 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 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba 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 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba 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 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba // 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 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba 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 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba } } 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 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba 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 6eb524a3886c..563bc463e5a3 100644 --- a/core/java/android/preference/VolumePreference.java +++ b/core/java/android/preference/VolumePreference.java @@ -176,6 +176,11 @@ public class VolumePreference extends SeekBarDialogPreference implements } @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 9f52142a5109..79d6bb4a062a 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -3956,9 +3956,7 @@ public final class Telephony { 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 final class Telephony { 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/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 9cc6b9f83ede..9244647432c7 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -44,6 +44,8 @@ public class FeatureFlagUtils { /** @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 @@ public class FeatureFlagUtils { 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/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java index c5f2299e4f83..104bc4347c29 100644 --- a/core/java/android/view/inputmethod/EditorInfo.java +++ b/core/java/android/view/inputmethod/EditorInfo.java @@ -22,13 +22,14 @@ import android.annotation.IntDef; 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 @@ public class EditorInfo implements InputType, Parcelable { * {@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 @@ public class EditorInfo implements InputType, Parcelable { * 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 @@ public class EditorInfo implements InputType, Parcelable { 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 @@ public class EditorInfo implements InputType, Parcelable { } 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 @@ public class EditorInfo implements InputType, Parcelable { 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 @@ public class EditorInfo implements InputType, Parcelable { 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/proto/android/stats/hdmi/enums.proto b/core/proto/android/stats/hdmi/enums.proto new file mode 100644 index 000000000000..acb8899fbdd9 --- /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 68e2891657ac..c9a4ab081a5b 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/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java index 93de03adfa84..5c410878c99d 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 @@ public class EditorInfoTest { 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/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 602ccfeca2f2..102c933dd84d 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -226,6 +226,7 @@ applications that come with the platform <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/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index f4b4f374bdc5..d060f6444463 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 @@ public class OneHandedController implements OneHanded { 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 @@ public class OneHandedController implements OneHanded { 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"); @@ -201,9 +204,8 @@ public class OneHandedController implements OneHanded { mTouchHandler = touchHandler; mTutorialHandler = tutorialHandler; mGestureHandler = gestureHandler; + mOverlayManager = overlayManager; - mOverlayManager = IOverlayManager.Stub.asInterface( - ServiceManager.getService(Context.OVERLAY_SERVICE)); mOffSetFraction = SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50) / 100.0f; mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( 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 1ce8b5445b37..3645f1e56f92 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 @@ package com.android.wm.shell.onehanded; 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 @@ public class OneHandedControllerTest extends OneHandedTestCase { OneHandedGestureHandler mMockGestureHandler; @Mock OneHandedTimeoutHandler mMockTimeoutHandler; + @Mock + IOverlayManager mMockOverlayManager; @Before public void setUp() throws Exception { @@ -73,7 +75,8 @@ public class OneHandedControllerTest extends OneHandedTestCase { 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 4a133d39291a..3341c9cbacb9 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 @@ package com.android.wm.shell.onehanded; import static org.mockito.Mockito.verify; +import android.content.om.IOverlayManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -44,6 +45,8 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { DisplayController mMockDisplayController; @Mock OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer; + @Mock + IOverlayManager mMockOverlayManager; @Before public void setUp() { @@ -56,11 +59,12 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { mMockDisplayAreaOrganizer, mTouchHandler, mTutorialHandler, - mGestureHandler); + mGestureHandler, + mMockOverlayManager); } @Test - public void testOneHandedManager_registerForDisplayAreaOrganizer() { + public void testRegisterForDisplayAreaOrganizer() { verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mTutorialHandler); } } diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt index 5f147c93f956..480e49415da6 100644 --- a/non-updatable-api/current.txt +++ b/non-updatable-api/current.txt @@ -44909,6 +44909,7 @@ package android.telephony { 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"; @@ -45136,6 +45137,8 @@ package android.telephony { 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 1e5d6cd5c1ff..82f2021af425 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -1532,12 +1532,16 @@ package android.bluetooth { 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 { @@ -8237,6 +8241,7 @@ package android.provider { 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"; diff --git a/packages/SystemUI/res/drawable/ic_move.xml b/packages/SystemUI/res/drawable/ic_move_magnification.xml index e82c9d0f23a9..ed97d0cc0522 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 25d63e3175d0..6ced97836358 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 e99245fa438f..23195af8bdea 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.SurfaceView; 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 class AdminSecondaryLockScreenController { } }; - 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 @@ public class AdminSecondaryLockScreenController { 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 88f4176f5eac..cc6df45c598f 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 abstract class KeyguardAbsKeyInputView extends LinearLayout 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 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout @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 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout 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 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout } } - @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 000000000000..d957628c52ab --- /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 be21d203411e..36d5543f1c01 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.Dependency; 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 @@ public class KeyguardDisplayManager { 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 @@ public class KeyguardDisplayManager { @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 @@ public class KeyguardDisplayManager { 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 @@ public class KeyguardDisplayManager { 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 @@ public class KeyguardDisplayManager { 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 @@ public class KeyguardDisplayManager { 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 7aabb17de90c..9ffa658da0e8 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java @@ -178,18 +178,18 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> /** 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 class KeyguardHostViewController extends ViewController<KeyguardHostView> } 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 000000000000..d42a53cc875e --- /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 000000000000..fbda818740e8 --- /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 a8b1451d92c7..1a0a4370fca4 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.util.TypedValue; 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 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp 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 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp 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 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp 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 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp update(); } - private void update() { + void update() { CharSequence status = mMessage; setVisibility(TextUtils.isEmpty(status) || !mBouncerVisible ? INVISIBLE : VISIBLE); setText(status); @@ -208,6 +148,9 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp 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 f056bdbb9706..1618e8e58055 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 @@ public class KeyguardMessageAreaController extends ViewController<KeyguardMessag 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 @@ public class KeyguardMessageAreaController extends ViewController<KeyguardMessag @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); + } + + 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 createing {@link com.android.keyguard.KeyguardMessageAreaController}. */ + /** 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 12ea1d586e10..580d7043a220 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java @@ -24,7 +24,6 @@ import android.view.animation.AnimationUtils; 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 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { 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 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { 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 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { 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 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { 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 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { }); } - @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 97317cf5580f..aaa5efec807e 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 class KeyguardPasswordView extends KeyguardAbsKeyInputView 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 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView @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 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView } @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 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView } } - @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 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView 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 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView } @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 000000000000..d34ea8c5e018 --- /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 c4a9fcb45284..bdcf467c2456 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 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit 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 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit */ 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 class KeyguardPatternView extends LinearLayout implements KeyguardSecurit 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 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit (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 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit } @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 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit } @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 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit 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 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit } } - @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 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit -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 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit @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 000000000000..3db9db7be00c --- /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 c7f27cf8a71a..7fa43116a7b1 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 @@ import com.android.systemui.R; /** * 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 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView @Override protected void resetState() { - setPasswordEntryEnabled(true); } @Override @@ -86,10 +81,10 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView @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 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView } } - 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 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView @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 000000000000..4d0ebfffbe04 --- /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 000000000000..6769436be8ef --- /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 05172279c4ed..41158296fa57 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.ime; 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.AnimatorListenerAdapter; 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.annotation.VisibleForTesting; 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 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe // 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 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe // 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 class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe 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 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe 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 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe } 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 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe } }); } - 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 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe 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 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe 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 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe 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 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe 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 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe 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 17f25bd08ef4..64676e55b038 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 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 class KeyguardSecurityContainerController extends ViewController<Keyguard /** */ 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 ac2160ecb4ae..c77c86711abf 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java @@ -18,13 +18,14 @@ package com.android.keyguard; 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 @@ public class KeyguardSecurityModel { /** * The different types of security available. - * @see KeyguardSecurityContainer#showSecurityScreen + * @see KeyguardSecurityContainerController#showSecurityScreen */ public enum SecurityMode { Invalid, // NULL state @@ -45,21 +46,15 @@ public class KeyguardSecurityModel { 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 43cef3acf147..ac00e9453c97 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java @@ -18,11 +18,9 @@ package com.android.keyguard; 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 @@ public interface KeyguardSecurityView { 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 @@ public interface KeyguardSecurityView { 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 @@ public interface KeyguardSecurityView { 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 ef9ba19fbb43..000000000000 --- 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 24da3ad46f23..b8439af6daaa 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java @@ -18,7 +18,6 @@ package com.android.keyguard; 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.view.ViewHierarchyEncoder; import android.widget.FrameLayout; import android.widget.ViewFlipper; -import com.android.internal.widget.LockPatternUtils; import com.android.systemui.R; /** @@ -39,7 +37,7 @@ import com.android.systemui.R; * 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 @@ public class KeyguardSecurityViewFlipper extends ViewFlipper implements Keyguard 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 000000000000..49530355a6fb --- /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 1c47aa0151f0..c0f9ce794628 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 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { 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 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { 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 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { 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 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { } @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 000000000000..cc8bf4f2d028 --- /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 5148dd709026..0d72c93e9041 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 @@ import com.android.systemui.R; * 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 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { 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 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { 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 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { 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 000000000000..a87374939ba6 --- /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 e59602b1cfff..425e50ed6397 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 @@ class LiftToActivateListener implements View.OnHoverListener { 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 b0457fce6a1a..2205fdd4267d 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.LayoutInflater; 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 @@ public class NumPadKey extends ViewGroup { } 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 b6010c8915e7..881108858b51 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 android.view.ViewGroup; 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 @@ public interface KeyguardBouncerModule { /** */ @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 ed78c94d45f9..832edf719dbb 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.NotificationFilter; 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 @@ public class Dependency { @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 @@ public class Dependency { 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 494a0f64cea4..714095631fdb 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.View; 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 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold 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 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold 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 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold 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 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold 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 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold * @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 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold /** 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 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold 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 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold /** * Gets the scale. + * * @return {@link Float#NaN} if the window is invisible. */ float getScale() { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 06c190f1964c..ba78485438ea 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.LayoutInflater; 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 @@ class UdfpsController implements DozeReceiver { 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 @@ class UdfpsController implements DozeReceiver { // 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 @@ class UdfpsController implements DozeReceiver { @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 @@ class UdfpsController implements DozeReceiver { // 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 @@ class UdfpsController implements DozeReceiver { // 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 @@ class UdfpsController implements DozeReceiver { } private void showUdfpsOverlay() { - mHandler.post(() -> { + mFgExecutor.execute(() -> { if (!mIsOverlayShowing) { try { Log.v(TAG, "showUdfpsOverlay | adding window"); @@ -211,7 +225,7 @@ class UdfpsController implements DozeReceiver { } private void hideUdfpsOverlay() { - mHandler.post(() -> { + mFgExecutor.execute(() -> { if (mIsOverlayShowing) { Log.v(TAG, "hideUdfpsOverlay | removing window"); mView.setOnTouchListener(null); @@ -228,7 +242,7 @@ class UdfpsController implements DozeReceiver { // 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 @@ class UdfpsController implements DozeReceiver { // 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 @@ class UdfpsController implements DozeReceiver { if (!mIsAodInterruptActive) { return; } - mHandler.removeCallbacks(mAodInterruptTimeoutAction); + if (mCancelAodTimeoutAction != null) { + mCancelAodTimeoutAction.run(); + mCancelAodTimeoutAction = null; + } mIsAodInterruptActive = false; onFingerUp(); } @@ -338,8 +356,7 @@ class UdfpsController implements DozeReceiver { 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/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 38e12a6ed5f8..eb431274b8a3 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 @@ public class DependencyProvider { /** */ @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 b35579d3624b..79925bad3cc7 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.ViewConfiguration; 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 @@ public class FrameworkServicesModule { @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 1ef806c8bd68..d5820f3e05e4 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 @@ public class DozeMachine { /** 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 b872b1a4219a..5aeb8df2028d 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 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi * --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 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi @Override public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { - mState = newState; switch (newState) { case INITIALIZED: case DOZE: @@ -112,10 +110,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi @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/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index 23270630c7da..01333f0a47d5 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.content.pm.ShortcutManager; 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.NotificationLogger; 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 @@ public interface NotificationsModule { 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 adda049951ac..811a72de093c 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 @@ public class ExpandableNotificationRow extends ActivatableNotificationView * 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 @@ public class ExpandableNotificationRow extends ActivatableNotificationView 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 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } /** - * 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 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } 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 eeac46a60ac8..7071b73c2ebf 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 @@ public class NotificationGuts extends FrameLayout { */ 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 @@ public class NotificationGuts extends FrameLayout { } 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/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index ab241e6721ee..703c214ed3ac 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 @@ -96,7 +96,6 @@ import com.android.systemui.statusbar.notification.row.ActivatableNotificationVi import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView; -import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import com.android.systemui.statusbar.notification.row.NotificationGuts; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationSnooze; @@ -156,7 +155,6 @@ public class NotificationStackScrollLayoutController { private final LayoutInflater mLayoutInflater; private final NotificationRemoteInputManager mRemoteInputManager; private final VisualStabilityManager mVisualStabilityManager; - private final NotificationBlockingHelperManager mNotificationBlockingHelperManager; private final ShadeController mShadeController; private final KeyguardMediaController mKeyguardMediaController; private final SysuiStatusBarStateController mStatusBarStateController; @@ -394,8 +392,6 @@ public class NotificationStackScrollLayoutController { return; } - boolean isBlockingHelperShown = false; - mView.removeDraggedView(view); mView.updateContinuousShadowDrawing(); @@ -405,13 +401,10 @@ public class NotificationStackScrollLayoutController { 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( @@ -582,7 +575,6 @@ public class NotificationStackScrollLayoutController { LayoutInflater layoutInflater, NotificationRemoteInputManager remoteInputManager, VisualStabilityManager visualStabilityManager, - NotificationBlockingHelperManager notificationBlockingHelperManager, ShadeController shadeController) { mAllowLongPress = allowLongPress; mNotificationGutsManager = notificationGutsManager; @@ -628,7 +620,6 @@ public class NotificationStackScrollLayoutController { mLayoutInflater = layoutInflater; mRemoteInputManager = remoteInputManager; mVisualStabilityManager = visualStabilityManager; - mNotificationBlockingHelperManager = notificationBlockingHelperManager; mShadeController = shadeController; } @@ -691,10 +682,6 @@ public class NotificationStackScrollLayoutController { mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded); mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation); - // Blocking helper manager wants to know the expanded state, update as well. - mView.addOnExpandedHeightChangedListener((height, unused) -> - mNotificationBlockingHelperManager.setNotificationShadeExpanded(height) - ); mTunerService.addTunable( (key, newValue) -> { diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java index eb8f065149c8..a6cd350b33ce 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.InflateException; 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 @@ public class InjectionInflationController { 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 64f8dbbb9e34..c7aa780fcacb 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 @@ import android.view.View.OnAttachStateChangeListener; * 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 @@ public abstract class ViewController<T extends View> { } 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 9be2d124026c..dffad6ccbea5 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.TestableLooper.RunWithLooper; 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 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { private ComponentName mComponentName; private Intent mServiceIntent; private TestableLooper mTestableLooper; - private ViewGroup mParent; + private KeyguardSecurityContainer mKeyguardSecurityContainer; @Mock private Handler mHandler; @@ -84,8 +82,8 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { 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 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { 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 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { SurfaceView v = verifySurfaceReady(); mTestController.hide(); - verify(mParent).removeView(v); + verify(mKeyguardSecurityContainer).removeView(v); assertThat(mContext.isBound(mComponentName)).isFalse(); } @@ -154,7 +153,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { 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 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { 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 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { } 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 000000000000..c2ade81a9877 --- /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 5999e2cdec78..e7930795c7f8 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.FrameLayout; 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 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { 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 000000000000..a7197cca530c --- /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 fc7b9a4b47d1..31fb25a7a89c 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 000000000000..c69ec1a254c3 --- /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 b4363cf215f1..000000000000 --- 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 000000000000..4944284698a0 --- /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 6666a926c68b..000000000000 --- 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 559284ac0672..ae159c73b99f 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 androidx.test.filters.SmallTest; 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 @@ public class KeyguardPresentationTest extends SysuiTestCase { @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 @@ public class KeyguardPresentationTest extends SysuiTestCase { 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 000000000000..cdb91ecfad89 --- /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 a867825e223d..854be1f76722 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 @@ package com.android.keyguard; 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 @@ import org.mockito.junit.MockitoRule; @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 000000000000..3b7f4b839853 --- /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 0431704778c3..79ec4f2c553a 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.testing.TestableLooper.RunWithLooper; 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 @@ public class KeyguardStatusViewTest extends SysuiTestCase { @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 0fe381746449..86b1e61d76b1 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.dump.DumpManager; 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 @@ public abstract class SysuiTestCase { // 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 f1f394e70689..539b321991ad 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 static org.mockito.Mockito.when; 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 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { 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 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { 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 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { 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/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java new file mode 100644 index 000000000000..87ec72fe0a01 --- /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 b85b1c237f6b..be0865d1b3e9 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 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { @Test public void testAod_usesLightSensor() { - mScreen.transitionTo(UNINITIALIZED, INITIALIZED); - mScreen.transitionTo(INITIALIZED, DOZE_AOD); mScreen.onScreenState(Display.STATE_DOZE); waitForSensorManager(); @@ -127,8 +125,6 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { @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 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { } @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 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { } @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 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { 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/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index f29b46c73e4d..8f1d71c80e2d 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.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; @@ -71,7 +70,6 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { boolean mHeadsUpAnimatingAway = false; @Rule public MockitoRule mockito = MockitoJUnit.rule(); - @Mock private NotificationBlockingHelperManager mBlockingHelperManager; @Before public void setUp() throws Exception { @@ -83,9 +81,6 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { mGroupRow = mNotificationTestHelper.createGroup(); mGroupRow.setHeadsUpAnimatingAwayListener( animatingAway -> mHeadsUpAnimatingAway = animatingAway); - mDependency.injectTestDependency( - NotificationBlockingHelperManager.class, - mBlockingHelperManager); } @Test @@ -257,30 +252,6 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { } @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 5bef2573b48a..bb85cec5c099 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 @@ -58,10 +58,8 @@ import com.android.systemui.statusbar.StatusBarState; 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; @@ -91,7 +89,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @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; @@ -120,9 +117,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { 1, UserHandle.USER_CURRENT); // Inject dependencies before initializing the layout - mDependency.injectTestDependency( - NotificationBlockingHelperManager.class, - mBlockingHelperManager); mDependency.injectTestDependency(SysuiStatusBarStateController.class, mBarState); mDependency.injectMockDependency(ShadeController.class); 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 74a57bf7cc55..01d49c269fb2 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 @@ -27,7 +27,6 @@ import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -65,7 +64,6 @@ import com.android.systemui.statusbar.notification.collection.legacy.VisualStabi import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView; -import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; @@ -87,8 +85,6 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.function.BiConsumer; - /** * Tests for {@link NotificationStackScrollLayoutController}. */ @@ -132,7 +128,6 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { @Mock private NotificationRemoteInputManager mRemoteInputManager; @Mock private RemoteInputController mRemoteInputController; @Mock private VisualStabilityManager mVisualStabilityManager; - @Mock private NotificationBlockingHelperManager mNotificationBlockingHelperManager; @Mock private ShadeController mShadeController; @Captor @@ -184,7 +179,6 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { mLayoutInflater, mRemoteInputManager, mVisualStabilityManager, - mNotificationBlockingHelperManager, mShadeController ); @@ -378,25 +372,6 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { any(ForegroundServiceDungeonView.class)); } - @Test - public void testSetExpandedHeight_blockingHelperManagerReceivedCallbacks() { - - ArgumentCaptor<BiConsumer<Float, Float>> - onExpandedHeightChangeListenerCaptor = ArgumentCaptor.forClass( - BiConsumer.class); - - mController.attach(mNotificationStackScrollLayout); - - verify(mNotificationStackScrollLayout, times(2)).addOnExpandedHeightChangedListener( - onExpandedHeightChangeListenerCaptor.capture()); - - for (BiConsumer<Float, Float> listener : - onExpandedHeightChangeListenerCaptor.getAllValues()) { - listener.accept(1f, 2f); - } - verify(mNotificationBlockingHelperManager).setNotificationShadeExpanded(1f); - } - private LogMaker logMatcher(int category, int type) { return argThat(new LogMatcher(category, type)); } diff --git a/packages/Tethering/apex/Android.bp b/packages/Tethering/apex/Android.bp index 67097a79e5c0..05243749f765 100644 --- a/packages/Tethering/apex/Android.bp +++ b/packages/Tethering/apex/Android.bp @@ -19,6 +19,7 @@ apex { 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 000000000000..d54f86148665 --- /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 000000000000..cc5af3127b02 --- /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/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 0d79240a4b59..4e405cc7f7ea 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -418,7 +418,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { 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/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 06f44b1bd388..a80111c2f781 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 @@ public class AppOpsService extends IAppOpsService.Stub { 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 7175489614ea..99dc58e20209 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 @@ public class Vpn { @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 class Vpn { } 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 @@ public class Vpn { 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 @@ public class Vpn { 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 @@ public class Vpn { 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 @@ public class Vpn { 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 @@ public class Vpn { 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 @@ public class Vpn { // 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 @@ public class Vpn { 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 @@ public class Vpn { // 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 @@ public class Vpn { || 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 @@ public class Vpn { 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 @@ public class Vpn { 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 @@ public class Vpn { 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 @@ public class Vpn { 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 @@ public class Vpn { } // 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 @@ public class Vpn { * 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 @@ public class Vpn { 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 @@ public class Vpn { * 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 @@ public class Vpn { 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 @@ public class Vpn { 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 @@ public class Vpn { * * <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 @@ public class Vpn { * * <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 @@ public class Vpn { final Set<UidRange> rangesToTellNetdToAdd; if (enforce) { final Set<UidRange> rangesThatShouldBeBlocked = - createUserAndRestrictedProfilesRanges(mUserHandle, + createUserAndRestrictedProfilesRanges(mUserId, /* allowedApplications */ null, /* disallowedApplications */ exemptedPackages); @@ -1909,7 +1909,7 @@ public class Vpn { 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 @@ public class Vpn { 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 class Vpn { 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 @@ public class Vpn { } 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 62a527b15c39..48efa5c31f2b 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2357,6 +2357,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { "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/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index e848bb7092da..0f0057905100 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 @@ public class UserManagerService extends IUserManager.Stub { 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); diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java index 8568d5fc1d64..4a90bbcc6623 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 @@ public class BarController { 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 @@ public class BarController { } 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 2ca849de7f22..0044d7498614 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; 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 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp void statusBarVisibilityChanged(int visibility) { mLastStatusBarVisibility = visibility; - visibility = getDisplayPolicy().adjustSystemUiVisibilityLw(visibility); updateStatusBarVisibilityLocked(visibility); } @@ -3769,32 +3767,16 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp 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 779f6b2d30cc..572c9b306047 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_INSET_DECOR; 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_DRAW_BA 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.graphics.Region; 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 @@ import java.util.function.Consumer; */ 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 @@ public class DisplayPolicy { /** 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver; + private final WindowManagerInternal.AppTransitionListener mAppTransitionListener; + private class PolicyHandler extends Handler { PolicyHandler(Looper looper) { @@ -472,16 +438,9 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { } }); 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { } @VisibleForTesting - StatusBarController getStatusBarController() { + BarController getStatusBarController() { return mStatusBarController; } @@ -1368,25 +1369,6 @@ public class DisplayPolicy { } /** - * 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { // 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 @@ public class DisplayPolicy { // 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { "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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { - 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 @@ public class DisplayPolicy { - 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 @@ public class DisplayPolicy { + 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 @@ public class DisplayPolicy { } if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame); - return mNavigationBarController.checkHiddenLw(); } private boolean canReceiveInput(WindowState win) { @@ -2059,9 +1966,6 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { + " 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { } 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 @@ public class DisplayPolicy { : lastFocusCanReceiveKeys ? mLastFocusedWindow : mTopFullscreenOpaqueWindowState; if (winCandidate == null) { - return 0; + return false; } } final WindowState win = winCandidate; @@ -3011,17 +2891,9 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { && 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 @@ public class DisplayPolicy { 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; - } - - 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); - } + return true; } - 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 @@ public class DisplayPolicy { } @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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { 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); - } - - vis = configureNavBarOpacity(vis, 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( - ITYPE_NAVIGATION_BAR); + appearance &= ~APPEARANCE_OPAQUE_STATUS_BARS; + } - final boolean transientStatusBarAllowed = getStatusBar() != null - && (notificationShadeHasFocus || (!mForceShowSystemBars - && (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky)))); + appearance = configureNavBarOpacity(appearance, dockedStackVisible, + freeformStackVisible, resizing, fullscreenDrawsNavBarBackground, + dockedDrawsNavigationBarBackground); - final boolean transientNavBarAllowed = mNavigationBar != null - && !mForceShowSystemBars && hideNavBarSysui && immersiveSticky; + final InsetsState requestedInsetsState = win.getRequestedInsetsState(); + final boolean requestHideNavBar = !requestedInsetsState.getSourceOrDefaultVisibility( + ITYPE_NAVIGATION_BAR); 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { 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; - } - - private int setNavBarOpaqueFlag(int visibility) { - return visibility & ~(View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT); - } - - private int setNavBarTranslucentFlag(int visibility) { - visibility &= ~View.NAVIGATION_BAR_TRANSPARENT; - return visibility | View.NAVIGATION_BAR_TRANSLUCENT; + return appearance; } - private int setNavBarTransparentFlag(int visibility) { - visibility &= ~View.NAVIGATION_BAR_TRANSLUCENT; - return visibility | View.NAVIGATION_BAR_TRANSPARENT; + private int setNavBarOpaqueFlag(int appearance) { + return appearance | APPEARANCE_OPAQUE_NAVIGATION_BARS; } - 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 @@ public class DisplayPolicy { // 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { 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 3564e0bce5f5..000000000000 --- 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/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java index 1bf9c2a0822e..6d40034c6000 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.job.JobInfo; 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 @@ public class JobStatusTest { } 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 1cf133a2bbf2..18bd6b17f340 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.Context; 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 @@ public class QuotaControllerTest { 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/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java index 83df40629085..c0d5f7b5aa9e 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.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; @@ -31,10 +38,11 @@ import androidx.test.InstrumentationRegistry; 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 @@ import java.util.ArrayList; @RunWith(JUnit4.class) public class ArcInitiationActionFromAvrTest { - private HdmiDeviceInfo mDeviceInfoForTests; + private Context mContextSpy; private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem; private HdmiCecController mHdmiCecController; private HdmiControlService mHdmiControlService; @@ -52,87 +60,62 @@ public class ArcInitiationActionFromAvrTest { 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<>(); + @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())); - 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: - } - } + 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 boolean isPowerStandby() { return false; } @Override - boolean isAddressAllocated() { - return true; + void wakeUp() { } @Override - Looper getServiceLooper() { - return mTestLooper.getLooper(); + PowerManager getPowerManager() { + return powerManager; } - }; - mHdmiCecLocalDeviceAudioSystem = - new HdmiCecLocalDeviceAudioSystem(mHdmiControlService) { @Override - HdmiDeviceInfo getDeviceInfo() { - return mDeviceInfoForTests; + AudioManager getAudioManager() { + return mAudioManager; } @Override - void setArcStatus(boolean enabled) { - // do nothing + boolean isAddressAllocated() { + return true; } @Override - protected boolean isSystemAudioActivated() { - return true; + Looper getServiceLooper() { + return mTestLooper.getLooper(); } }; + mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService) { + @Override + protected void setPreferredAddress(int addr) { + } + }; + mHdmiCecLocalDeviceAudioSystem.init(); Looper looper = mTestLooper.getLooper(); mHdmiControlService.setIoLooper(looper); @@ -150,18 +133,88 @@ public class ArcInitiationActionFromAvrTest { 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(); + } - assertThat(mArcInitSent).isTrue(); - assertThat(mRequestActiveSourceSent).isTrue(); + @Test + public void arcInitiation_terminated() { + mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction); + mTestLooper.dispatchAll(); + + 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 dc326eefbc9b..f986a70804f6 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.InstrumentationRegistry; 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 @@ import org.junit.runners.JUnit4; @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())); + + 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 wakeUp() { + } - HdmiControlService hdmiControlService = - new HdmiControlService(mInstrumentation.getTargetContext()) { @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"); - } + PowerManager getPowerManager() { + return powerManager; + } + + @Override + AudioManager getAudioManager() { + return mAudioManager; } @Override @@ -97,55 +111,71 @@ public class ArcTerminationActionFromAvrTest { } }; - 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.dispatchAll(); + HdmiCecMessage terminateArc = HdmiCecMessageBuilder.buildTerminateArc( + Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_TV); + + assertThat(mNativeWrapper.getResultMessages()).contains(terminateArc); - mTestLooper.moveTimeForward(1000); + HdmiCecMessage arcTerminatedResponse = HdmiCecMessageBuilder.buildReportArcTerminated( + Constants.ADDR_TV, Constants.ADDR_AUDIO_SYSTEM); + + mHdmiControlService.handleCecCommand(arcTerminatedResponse); mTestLooper.dispatchAll(); - assertThat(mArcEnabled).isFalse(); + + 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 7538468fbe31..01f0a3d398df 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.android.server.hdmi.HdmiCecController.NativeWrapper; 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 @@ final class FakeNativeWrapper implements NativeWrapper { }; 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 @@ final class FakeNativeWrapper implements NativeWrapper { 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 @@ final class FakeNativeWrapper implements NativeWrapper { 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 94e40413f9f8..951118125f6e 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 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { 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 b77d21c0f711..a55423a7baf6 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 @@ package com.android.server.wm; 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 @@ public class DisplayPolicyTests extends WindowTestsBase { 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 @@ public class DisplayPolicyTests extends WindowTestsBase { 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 @@ public class DisplayPolicyTests extends WindowTestsBase { @Test public void testUpdateLightNavigationBarLw() { + DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); final WindowState opaqueDarkNavBar = createOpaqueFullscreen(false); final WindowState opaqueLightNavBar = createOpaqueFullscreen(true); @@ -171,50 +170,50 @@ public class DisplayPolicyTests extends WindowTestsBase { 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 af8cb02a86fe..94ffcdab4fa7 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.util.Pair; 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 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { 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 edcf0d4f5501..f1540731da8b 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 @@ public class SizeCompatTests extends WindowTestsBase { 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/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 4de1abfee7fc..829c746c1b5f 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -32,6 +32,7 @@ import android.os.RemoteException; 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 class CarrierConfigManager { 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 @@ public class CarrierConfigManager { "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 @@ public class CarrierConfigManager { * 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_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_DISABLE_DUN_APN_WHILE_ROAMING = - "disable_dun_apn_while_roaming"; + 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 @@ public class CarrierConfigManager { 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 @@ public class CarrierConfigManager { "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/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java index 2b3072eefe2e..da7311c08307 100644 --- a/telephony/java/android/telephony/ims/ImsService.java +++ b/telephony/java/android/telephony/ims/ImsService.java @@ -137,18 +137,30 @@ public class ImsService extends Service { } @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 @@ public class ImsService extends Service { 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 @@ public class ImsService extends Service { } } - 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 @@ public class ImsService extends Service { } } - 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 @@ public class ImsService extends Service { } } - 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 @@ public class ImsService extends Service { + 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 c7da681b86a3..c956cbcc816c 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl @@ -31,12 +31,14 @@ import com.android.ims.internal.IImsFeatureStatusCallback; */ 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 eafbb14539f5..41d1d726b3f4 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.app.Service; 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 @@ public class ImsService extends Service { protected final IBinder mImsServiceController = new IImsServiceController.Stub() { @Override - public IImsMMTelFeature createEmergencyMMTelFeature(int slotId, - IImsFeatureStatusCallback c) { - return createEmergencyMMTelFeatureInternal(slotId, c); + public IImsMMTelFeature createEmergencyMMTelFeature(int slotId) { + return createEmergencyMMTelFeatureInternal(slotId); } @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) - throws RemoteException { - ImsService.this.removeImsFeature(slotId, featureType, c); + public void removeImsFeature(int slotId, int featureType) { + ImsService.this.removeImsFeature(slotId, featureType); + } + + @Override + public void addFeatureStatusCallback(int slotId, int featureType, + IImsFeatureStatusCallback c) { + addImsFeatureStatusCallback(slotId, featureType, c); + } + + @Override + public void removeFeatureStatusCallback(int slotId, int featureType, + IImsFeatureStatusCallback c) { + removeImsFeatureStatusCallback(slotId, featureType, c); } }; @@ -137,46 +146,40 @@ public class ImsService extends Service { 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 @@ public class ImsService extends Service { } } - 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 @@ public class ImsService extends Service { + 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 857089fac33a..e9528f474082 100644 --- a/telephony/java/com/android/ims/internal/IImsServiceController.aidl +++ b/telephony/java/com/android/ims/internal/IImsServiceController.aidl @@ -25,8 +25,10 @@ import com.android.ims.internal.IImsRcsFeature; * {@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); } |