Merge "Adding support for nested LayerDrawables in GhostedViewLaunchAnimatorController" into main
diff --git a/Android.bp b/Android.bp
index f6a9328..b139b7e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -132,6 +132,7 @@
":libcamera_client_aidl",
":libcamera_client_framework_aidl",
":libupdate_engine_aidl",
+ ":libupdate_engine_stable-V2-java-source",
":logd_aidl",
":resourcemanager_aidl",
":storaged_aidl",
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 117faa2..d59775f 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -132,6 +132,9 @@
},
{
"name": "vts_treble_vintf_vendor_test"
+ },
+ {
+ "name": "CtsStrictJavaPackagesTestCases"
}
],
"postsubmit-ravenwood": [
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 28b2d4b..50c9fd3 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -900,6 +900,15 @@
],
api_levels_sdk_type: "system",
extensions_info_file: ":sdk-extensions-info",
+ dists: [
+ // Make the api-versions.xml file for the system API available in the
+ // sdk build target.
+ {
+ targets: ["sdk"],
+ dest: "api-versions_system.xml",
+ tag: ".api_versions.xml",
+ },
+ ],
}
// This module can be built with:
diff --git a/core/api/current.txt b/core/api/current.txt
index 5abb92b..c7b921c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -22102,7 +22102,7 @@
}
@FlaggedApi("android.media.audio.loudness_configurator_api") public class LoudnessCodecConfigurator {
- method @FlaggedApi("android.media.audio.loudness_configurator_api") public void addMediaCodec(@NonNull android.media.MediaCodec);
+ method @FlaggedApi("android.media.audio.loudness_configurator_api") public boolean addMediaCodec(@NonNull android.media.MediaCodec);
method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public static android.media.LoudnessCodecConfigurator create();
method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public static android.media.LoudnessCodecConfigurator create(@NonNull java.util.concurrent.Executor, @NonNull android.media.LoudnessCodecConfigurator.OnLoudnessCodecUpdateListener);
method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public android.os.Bundle getLoudnessCodecParams(@NonNull android.media.AudioTrack, @NonNull android.media.MediaCodec);
@@ -28825,6 +28825,8 @@
method public boolean isSecureNfcEnabled();
method public boolean isSecureNfcSupported();
method @FlaggedApi("android.nfc.enable_nfc_charging") public boolean isWlcEnabled();
+ method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void resetDiscoveryTechnology(@NonNull android.app.Activity);
+ method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void setDiscoveryTechnology(@NonNull android.app.Activity, int, int);
field public static final String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED";
field public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
field @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static final String ACTION_PREFERRED_PAYMENT_CHANGED = "android.nfc.action.PREFERRED_PAYMENT_CHANGED";
@@ -28840,6 +28842,13 @@
field public static final String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
field public static final String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME";
field public static final String EXTRA_TAG = "android.nfc.extra.TAG";
+ field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_DISABLE = 0; // 0x0
+ field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_KEEP = -1; // 0xffffffff
+ field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_A = 1; // 0x1
+ field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_B = 2; // 0x2
+ field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_F = 4; // 0x4
+ field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_READER_DISABLE = 0; // 0x0
+ field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_READER_KEEP = -1; // 0xffffffff
field public static final int FLAG_READER_NFC_A = 1; // 0x1
field public static final int FLAG_READER_NFC_B = 2; // 0x2
field public static final int FLAG_READER_NFC_BARCODE = 16; // 0x10
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 0d4169f..9077d02 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -95,7 +95,7 @@
field public static final String BYPASS_ROLE_QUALIFICATION = "android.permission.BYPASS_ROLE_QUALIFICATION";
field public static final String CALL_AUDIO_INTERCEPTION = "android.permission.CALL_AUDIO_INTERCEPTION";
field public static final String CAMERA_DISABLE_TRANSMIT_LED = "android.permission.CAMERA_DISABLE_TRANSMIT_LED";
- field public static final String CAMERA_HEADLESS_SYSTEM_USER = "android.permission.CAMERA_HEADLESS_SYSTEM_USER";
+ field @FlaggedApi("com.android.internal.camera.flags.camera_hsum_permission") public static final String CAMERA_HEADLESS_SYSTEM_USER = "android.permission.CAMERA_HEADLESS_SYSTEM_USER";
field public static final String CAMERA_OPEN_CLOSE_LISTENER = "android.permission.CAMERA_OPEN_CLOSE_LISTENER";
field public static final String CAPTURE_AUDIO_HOTWORD = "android.permission.CAPTURE_AUDIO_HOTWORD";
field public static final String CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD = "android.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD";
@@ -473,7 +473,7 @@
field public static final int config_defaultCallScreening = 17039398; // 0x1040026
field public static final int config_defaultDialer = 17039395; // 0x1040023
field public static final int config_defaultNotes = 17039429; // 0x1040045
- field public static final int config_defaultRetailDemo;
+ field @FlaggedApi("android.permission.flags.retail_demo_role_enabled") public static final int config_defaultRetailDemo;
field public static final int config_defaultSms = 17039396; // 0x1040024
field public static final int config_devicePolicyManagement = 17039421; // 0x104003d
field public static final int config_feedbackIntentExtraKey = 17039391; // 0x104001f
@@ -4141,7 +4141,7 @@
field public static final int PROTECTION_FLAG_MODULE = 4194304; // 0x400000
field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
field public static final int PROTECTION_FLAG_RECENTS = 33554432; // 0x2000000
- field @Deprecated public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000
+ field public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000
field public static final int PROTECTION_FLAG_ROLE = 67108864; // 0x4000000
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
@@ -10571,7 +10571,7 @@
method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static int rebootAndApply(@NonNull android.content.Context, @NonNull String, boolean) throws java.io.IOException;
method @RequiresPermission(allOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootWipeAb(android.content.Context, java.io.File, String) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException;
- method public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException;
+ method @Deprecated public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException;
field public static final int RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME = 2000; // 0x7d0
field public static final int RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED = 3000; // 0xbb8
field public static final int RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE = 5000; // 0x1388
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 5d4d5e2..f9583d2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -24,7 +24,9 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.inMultiWindowMode;
import static android.os.Process.myUid;
+
import static com.android.sdksandbox.flags.Flags.sandboxActivitySdkBasedContext;
+
import static java.lang.Character.MIN_VALUE;
import android.annotation.AnimRes;
@@ -45,6 +47,7 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UiContext;
+import android.app.ActivityOptions.SceneTransitionInfo;
import android.app.VoiceInteractor.Request;
import android.app.admin.DevicePolicyManager;
import android.app.assist.AssistContent;
@@ -930,8 +933,8 @@
@UnsupportedAppUsage
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
- /** The options for scene transition. */
- ActivityOptions mPendingOptions;
+ /** The scene transition info. */
+ SceneTransitionInfo mSceneTransitionInfo;
/** Whether this activity was launched from a bubble. **/
boolean mLaunchedFromBubble;
@@ -5807,10 +5810,9 @@
private Bundle transferSpringboardActivityOptions(@Nullable Bundle options) {
if (options == null && (mWindow != null && !mWindow.isActive())) {
- final ActivityOptions activityOptions = getActivityOptions();
- if (activityOptions != null &&
- activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
- return activityOptions.toBundle();
+ final SceneTransitionInfo info = getSceneTransitionInfo();
+ if (info != null) {
+ return ActivityOptions.makeBasic().setSceneTransitionInfo(info).toBundle();
}
}
return options;
@@ -8079,8 +8081,10 @@
*
* @param callback the method to call when all visible activities behind this one have been
* drawn and it is safe to make this activity translucent again.
- * @param options activity options delivered to the activity below this one. The options
- * are retrieved using {@link #getActivityOptions}.
+ * @param options activity options that created from
+ * {@link ActivityOptions#makeSceneTransitionAnimation} which will be converted to
+ * {@link SceneTransitionInfo} and delivered to the activity below this one. The
+ * options are retrieved using {@link #getSceneTransitionInfo}.
* @return <code>true</code> if Window was opaque and will become translucent or
* <code>false</code> if window was translucent and no change needed to be made.
*
@@ -8116,27 +8120,27 @@
}
/** @hide */
- public void onNewActivityOptions(ActivityOptions options) {
- mActivityTransitionState.setEnterActivityOptions(this, options);
+ public void onNewSceneTransitionInfo(ActivityOptions.SceneTransitionInfo info) {
+ mActivityTransitionState.setEnterSceneTransitionInfo(this, info);
if (!mStopped) {
mActivityTransitionState.enterReady(this);
}
}
/**
- * Takes the ActivityOptions passed in from the launching activity or passed back
+ * Takes the {@link SceneTransitionInfo} passed in from the launching activity or passed back
* from an activity launched by this activity in its call to {@link
* #convertToTranslucent(TranslucentConversionListener, ActivityOptions)}
*
- * @return The ActivityOptions passed to {@link #convertToTranslucent}.
+ * @return The {@link SceneTransitionInfo} which based on the ActivityOptions that originally
+ * passed to {@link #convertToTranslucent}.
* @hide
*/
- @UnsupportedAppUsage
- ActivityOptions getActivityOptions() {
- final ActivityOptions options = mPendingOptions;
- // The option only applies once.
- mPendingOptions = null;
- return options;
+ SceneTransitionInfo getSceneTransitionInfo() {
+ final SceneTransitionInfo sceneTransitionInfo = mSceneTransitionInfo;
+ // The info only applies once.
+ mSceneTransitionInfo = null;
+ return sceneTransitionInfo;
}
/**
@@ -8780,7 +8784,7 @@
mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
com.android.internal.R.styleable.Window_windowNoDisplay, false);
mFragments.dispatchActivityCreated();
- mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
+ mActivityTransitionState.setEnterSceneTransitionInfo(this, getSceneTransitionInfo());
dispatchActivityPostCreated(icicle);
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
@@ -8798,7 +8802,7 @@
+ mComponent.getClassName());
}
dispatchActivityPreStarted();
- mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
+ mActivityTransitionState.setEnterSceneTransitionInfo(this, getSceneTransitionInfo());
mFragments.noteStateNotSaved();
mCalled = false;
mFragments.execPendingActions();
diff --git a/core/java/android/app/ActivityOptions.aidl b/core/java/android/app/ActivityOptions.aidl
new file mode 100644
index 0000000..bd5cd88
--- /dev/null
+++ b/core/java/android/app/ActivityOptions.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+/** @hide */
+parcelable ActivityOptions.SceneTransitionInfo;
\ No newline at end of file
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 9c279c3..8af7ed1 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -357,22 +357,7 @@
private static final String KEY_APPLY_NO_USER_ACTION_FLAG_FOR_SHORTCUT =
"android:activity.applyNoUserActionFlagForShortcut";
- /**
- * For Activity transitions, the calling Activity's TransitionListener used to
- * notify the called Activity when the shared element and the exit transitions
- * complete.
- */
- private static final String KEY_TRANSITION_COMPLETE_LISTENER
- = "android:activity.transitionCompleteListener";
-
- private static final String KEY_TRANSITION_IS_RETURNING
- = "android:activity.transitionIsReturning";
- private static final String KEY_TRANSITION_SHARED_ELEMENTS
- = "android:activity.sharedElementNames";
- private static final String KEY_RESULT_DATA = "android:activity.resultData";
- private static final String KEY_RESULT_CODE = "android:activity.resultCode";
- private static final String KEY_EXIT_COORDINATOR_INDEX
- = "android:activity.exitCoordinatorIndex";
+ private static final String KEY_SCENE_TRANSITION_INFO = "android:activity.sceneTransitionInfo";
/** See {@link SourceInfo}. */
private static final String KEY_SOURCE_INFO = "android.activity.sourceInfo";
@@ -472,12 +457,7 @@
private int mHeight;
private IRemoteCallback mAnimationStartedListener;
private IRemoteCallback mAnimationFinishedListener;
- private ResultReceiver mTransitionReceiver;
- private boolean mIsReturning;
- private ArrayList<String> mSharedElementNames;
- private Intent mResultData;
- private int mResultCode;
- private int mExitCoordinatorIndex;
+ private SceneTransitionInfo mSceneTransitionInfo;
private PendingIntent mUsageTimeReport;
private int mLaunchDisplayId = INVALID_DISPLAY;
private int mCallerDisplayId = INVALID_DISPLAY;
@@ -1006,8 +986,11 @@
ExitTransitionCoordinator exit = makeSceneTransitionAnimation(
new ActivityExitTransitionCallbacks(activity), activity.mExitTransitionListener,
activity.getWindow(), opts, sharedElements);
- opts.mExitCoordinatorIndex =
- activity.mActivityTransitionState.addExitTransitionCoordinator(exit);
+ final SceneTransitionInfo info = opts.getSceneTransitionInfo();
+ if (info != null) {
+ info.setExitCoordinatorKey(
+ activity.mActivityTransitionState.addExitTransitionCoordinator(exit));
+ }
return opts;
}
@@ -1029,13 +1012,16 @@
ActivityOptions opts = new ActivityOptions();
ExitTransitionCoordinator exit = makeSceneTransitionAnimation(
exitCallbacks, callback, window, opts, sharedElements);
- opts.mExitCoordinatorIndex = -1;
+ final SceneTransitionInfo info = opts.getSceneTransitionInfo();
+ if (info != null) {
+ info.setExitCoordinatorKey(-1);
+ }
return Pair.create(opts, exit);
}
/**
- * This method should be called when the
- * {@link #startSharedElementAnimation(Window, ExitTransitionCallbacks, Pair[])}
+ * This method should be called when the {@link #startSharedElementAnimation(Window,
+ * ExitTransitionCallbacks, SharedElementCallback, Pair[])}
* animation must be stopped and the Views reset. This can happen if there was an error
* from startActivity or a springboard activity and the animation should stop and reset.
*
@@ -1088,9 +1074,11 @@
ExitTransitionCoordinator exit = new ExitTransitionCoordinator(exitCallbacks, window,
callback, names, names, views, false);
- opts.mTransitionReceiver = exit;
- opts.mSharedElementNames = names;
- opts.mIsReturning = false;
+ final SceneTransitionInfo info = new SceneTransitionInfo();
+ info.setResultReceiver(exit);
+ info.setSharedElementNames(names);
+ info.setReturning(false);
+ opts.setSceneTransitionInfo(info);
return exit;
}
@@ -1111,17 +1099,20 @@
int resultCode, Intent resultData) {
ActivityOptions opts = new ActivityOptions();
opts.mAnimationType = ANIM_SCENE_TRANSITION;
- opts.mSharedElementNames = sharedElementNames;
- opts.mTransitionReceiver = exitCoordinator;
- opts.mIsReturning = true;
- opts.mResultCode = resultCode;
- opts.mResultData = resultData;
+ final SceneTransitionInfo info = new SceneTransitionInfo();
+ info.setSharedElementNames(sharedElementNames);
+ info.setResultReceiver(exitCoordinator);
+ info.setReturning(true);
+ info.setResultCode(resultCode);
+ info.setResultData(resultData);
if (activity == null) {
- opts.mExitCoordinatorIndex = -1;
+ info.setExitCoordinatorKey(-1);
} else {
- opts.mExitCoordinatorIndex =
- activity.mActivityTransitionState.addExitTransitionCoordinator(exitCoordinator);
+ info.setExitCoordinatorKey(
+ activity.mActivityTransitionState.addExitTransitionCoordinator(
+ exitCoordinator));
}
+ opts.setSceneTransitionInfo(info);
return opts;
}
@@ -1269,12 +1260,8 @@
break;
case ANIM_SCENE_TRANSITION:
- mTransitionReceiver = opts.getParcelable(KEY_TRANSITION_COMPLETE_LISTENER, android.os.ResultReceiver.class);
- mIsReturning = opts.getBoolean(KEY_TRANSITION_IS_RETURNING, false);
- mSharedElementNames = opts.getStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS);
- mResultData = opts.getParcelable(KEY_RESULT_DATA, android.content.Intent.class);
- mResultCode = opts.getInt(KEY_RESULT_CODE);
- mExitCoordinatorIndex = opts.getInt(KEY_EXIT_COORDINATOR_INDEX);
+ mSceneTransitionInfo = opts.getParcelable(KEY_SCENE_TRANSITION_INFO,
+ SceneTransitionInfo.class);
break;
}
mLockTaskMode = opts.getBoolean(KEY_LOCK_TASK_MODE, false);
@@ -1437,9 +1424,6 @@
}
/** @hide */
- public int getExitCoordinatorKey() { return mExitCoordinatorIndex; }
-
- /** @hide */
public void abort() {
if (mAnimationStartedListener != null) {
try {
@@ -1450,35 +1434,17 @@
}
/** @hide */
- public boolean isReturning() {
- return mIsReturning;
- }
-
- /**
- * Returns whether or not the ActivityOptions was created with
- * {@link #startSharedElementAnimation(Window, Pair[])}.
- *
- * @hide
- */
- boolean isCrossTask() {
- return mExitCoordinatorIndex < 0;
+ public ActivityOptions setSceneTransitionInfo(SceneTransitionInfo info) {
+ mSceneTransitionInfo = info;
+ return this;
}
/** @hide */
- public ArrayList<String> getSharedElementNames() {
- return mSharedElementNames;
+ public SceneTransitionInfo getSceneTransitionInfo() {
+ return mSceneTransitionInfo;
}
/** @hide */
- public ResultReceiver getResultReceiver() { return mTransitionReceiver; }
-
- /** @hide */
- public int getResultCode() { return mResultCode; }
-
- /** @hide */
- public Intent getResultData() { return mResultData; }
-
- /** @hide */
public PendingIntent getUsageTimeReport() {
return mUsageTimeReport;
}
@@ -2102,12 +2068,7 @@
mPackageName = otherOptions.mPackageName;
}
mUsageTimeReport = otherOptions.mUsageTimeReport;
- mTransitionReceiver = null;
- mSharedElementNames = null;
- mIsReturning = false;
- mResultData = null;
- mResultCode = 0;
- mExitCoordinatorIndex = 0;
+ mSceneTransitionInfo = null;
mAnimationType = otherOptions.mAnimationType;
switch (otherOptions.mAnimationType) {
case ANIM_CUSTOM:
@@ -2157,14 +2118,9 @@
mAnimationStartedListener = otherOptions.mAnimationStartedListener;
break;
case ANIM_SCENE_TRANSITION:
- mTransitionReceiver = otherOptions.mTransitionReceiver;
- mSharedElementNames = otherOptions.mSharedElementNames;
- mIsReturning = otherOptions.mIsReturning;
+ mSceneTransitionInfo = otherOptions.mSceneTransitionInfo;
mThumbnail = null;
mAnimationStartedListener = null;
- mResultData = otherOptions.mResultData;
- mResultCode = otherOptions.mResultCode;
- mExitCoordinatorIndex = otherOptions.mExitCoordinatorIndex;
break;
}
mLockTaskMode = otherOptions.mLockTaskMode;
@@ -2240,14 +2196,9 @@
!= null ? mAnimationStartedListener.asBinder() : null);
break;
case ANIM_SCENE_TRANSITION:
- if (mTransitionReceiver != null) {
- b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionReceiver);
+ if (mSceneTransitionInfo != null) {
+ b.putParcelable(KEY_SCENE_TRANSITION_INFO, mSceneTransitionInfo);
}
- b.putBoolean(KEY_TRANSITION_IS_RETURNING, mIsReturning);
- b.putStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS, mSharedElementNames);
- b.putParcelable(KEY_RESULT_DATA, mResultData);
- b.putInt(KEY_RESULT_CODE, mResultCode);
- b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex);
break;
}
if (mLockTaskMode) {
@@ -2607,4 +2558,124 @@
}
};
}
+
+ /**
+ * This class contains necessary information for Activity Scene Transition.
+ *
+ * @hide
+ */
+ public static class SceneTransitionInfo implements Parcelable {
+ private boolean mIsReturning;
+ private int mResultCode;
+ @Nullable
+ private Intent mResultData;
+ @Nullable
+ private ArrayList<String> mSharedElementNames;
+ @Nullable
+ private ResultReceiver mResultReceiver;
+ private int mExitCoordinatorIndex;
+
+ public SceneTransitionInfo() {
+ }
+
+ SceneTransitionInfo(Parcel in) {
+ mIsReturning = in.readBoolean();
+ mResultCode = in.readInt();
+ mResultData = in.readTypedObject(Intent.CREATOR);
+ mSharedElementNames = in.createStringArrayList();
+ mResultReceiver = in.readTypedObject(ResultReceiver.CREATOR);
+ mExitCoordinatorIndex = in.readInt();
+ }
+
+ public static final Creator<SceneTransitionInfo> CREATOR = new Creator<>() {
+ @Override
+ public SceneTransitionInfo createFromParcel(Parcel in) {
+ return new SceneTransitionInfo(in);
+ }
+
+ @Override
+ public SceneTransitionInfo[] newArray(int size) {
+ return new SceneTransitionInfo[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBoolean(mIsReturning);
+ dest.writeInt(mResultCode);
+ dest.writeTypedObject(mResultData, flags);
+ dest.writeStringList(mSharedElementNames);
+ dest.writeTypedObject(mResultReceiver, flags);
+ dest.writeInt(mExitCoordinatorIndex);
+ }
+
+ public void setReturning(boolean isReturning) {
+ mIsReturning = isReturning;
+ }
+
+ public boolean isReturning() {
+ return mIsReturning;
+ }
+
+ public void setResultCode(int resultCode) {
+ mResultCode = resultCode;
+ }
+
+ public int getResultCode() {
+ return mResultCode;
+ }
+
+ public void setResultData(Intent resultData) {
+ mResultData = resultData;
+ }
+
+ @Nullable
+ public Intent getResultData() {
+ return mResultData;
+ }
+
+ public void setSharedElementNames(ArrayList<String> sharedElementNames) {
+ mSharedElementNames = sharedElementNames;
+ }
+
+ @Nullable
+ public ArrayList<String> getSharedElementNames() {
+ return mSharedElementNames;
+ }
+
+ public void setResultReceiver(ResultReceiver resultReceiver) {
+ mResultReceiver = resultReceiver;
+ }
+
+ @Nullable
+ public ResultReceiver getResultReceiver() {
+ return mResultReceiver;
+ }
+
+ public void setExitCoordinatorKey(int exitCoordinatorKey) {
+ mExitCoordinatorIndex = exitCoordinatorKey;
+ }
+
+ public int getExitCoordinatorKey() {
+ return mExitCoordinatorIndex;
+ }
+
+ boolean isCrossTask() {
+ return mExitCoordinatorIndex < 0;
+ }
+
+ @Override
+ public String toString() {
+ return "SceneTransitionInfo, mIsReturning=" + mIsReturning
+ + ", mResultCode=" + mResultCode + ", mResultData=" + mResultData
+ + ", mSharedElementNames=" + mSharedElementNames
+ + ", mTransitionReceiver=" + mResultReceiver
+ + ", mExitCoordinatorIndex=" + mExitCoordinatorIndex;
+ }
+ }
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 7e5326e..949e2ba 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -43,6 +43,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityOptions.SceneTransitionInfo;
import android.app.RemoteServiceException.BadForegroundServiceNotificationException;
import android.app.RemoteServiceException.BadUserInitiatedJobNotificationException;
import android.app.RemoteServiceException.CannotPostForegroundServiceNotificationException;
@@ -632,8 +633,8 @@
@UnsupportedAppUsage
boolean mPreserveWindow;
- /** The options for scene transition. */
- ActivityOptions mActivityOptions;
+ /** The scene transition info. */
+ SceneTransitionInfo mSceneTransitionInfo;
/** Whether this activiy was launched from a bubble. */
boolean mLaunchedFromBubble;
@@ -660,7 +661,7 @@
ActivityInfo info, Configuration overrideConfig,
String referrer, IVoiceInteractor voiceInteractor, Bundle state,
PersistableBundle persistentState, List<ResultInfo> pendingResults,
- List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions,
+ List<ReferrerIntent> pendingNewIntents, SceneTransitionInfo sceneTransitionInfo,
boolean isForward, ProfilerInfo profilerInfo, ClientTransactionHandler client,
IBinder assistToken, IBinder shareableActivityToken, boolean launchedFromBubble,
IBinder taskFragmentToken) {
@@ -680,7 +681,7 @@
this.profilerInfo = profilerInfo;
this.overrideConfig = overrideConfig;
this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo);
- mActivityOptions = activityOptions;
+ mSceneTransitionInfo = sceneTransitionInfo;
mLaunchedFromBubble = launchedFromBubble;
mTaskFragmentToken = taskFragmentToken;
init();
@@ -1960,9 +1961,9 @@
sendMessage(H.TRANSLUCENT_CONVERSION_COMPLETE, token, drawComplete ? 1 : 0);
}
- public void scheduleOnNewActivityOptions(IBinder token, Bundle options) {
- sendMessage(H.ON_NEW_ACTIVITY_OPTIONS,
- new Pair<IBinder, ActivityOptions>(token, ActivityOptions.fromBundle(options)));
+ public void scheduleOnNewSceneTransitionInfo(IBinder token, SceneTransitionInfo info) {
+ sendMessage(H.ON_NEW_SCENE_TRANSITION_INFO,
+ new Pair<IBinder, SceneTransitionInfo>(token, info));
}
public void setProcessState(int state) {
@@ -2258,7 +2259,7 @@
public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144;
@UnsupportedAppUsage
public static final int INSTALL_PROVIDER = 145;
- public static final int ON_NEW_ACTIVITY_OPTIONS = 146;
+ public static final int ON_NEW_SCENE_TRANSITION_INFO = 146;
@UnsupportedAppUsage
public static final int ENTER_ANIMATION_COMPLETE = 149;
public static final int START_BINDER_TRACKING = 150;
@@ -2314,7 +2315,7 @@
case REQUEST_ASSIST_CONTEXT_EXTRAS: return "REQUEST_ASSIST_CONTEXT_EXTRAS";
case TRANSLUCENT_CONVERSION_COMPLETE: return "TRANSLUCENT_CONVERSION_COMPLETE";
case INSTALL_PROVIDER: return "INSTALL_PROVIDER";
- case ON_NEW_ACTIVITY_OPTIONS: return "ON_NEW_ACTIVITY_OPTIONS";
+ case ON_NEW_SCENE_TRANSITION_INFO: return "ON_NEW_SCENE_TRANSITION_INFO";
case ENTER_ANIMATION_COMPLETE: return "ENTER_ANIMATION_COMPLETE";
case LOCAL_VOICE_INTERACTION_STARTED: return "LOCAL_VOICE_INTERACTION_STARTED";
case ATTACH_AGENT: return "ATTACH_AGENT";
@@ -2520,9 +2521,10 @@
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
break;
- case ON_NEW_ACTIVITY_OPTIONS:
- Pair<IBinder, ActivityOptions> pair = (Pair<IBinder, ActivityOptions>) msg.obj;
- onNewActivityOptions(pair.first, pair.second);
+ case ON_NEW_SCENE_TRANSITION_INFO:
+ Pair<IBinder, SceneTransitionInfo> pair =
+ (Pair<IBinder, SceneTransitionInfo>) msg.obj;
+ onNewSceneTransitionInfo(pair.first, pair.second);
break;
case ENTER_ANIMATION_COMPLETE:
handleEnterAnimationComplete((IBinder) msg.obj);
@@ -3921,9 +3923,9 @@
activity.setTheme(theme);
}
- if (r.mActivityOptions != null) {
- activity.mPendingOptions = r.mActivityOptions;
- r.mActivityOptions = null;
+ if (r.mSceneTransitionInfo != null) {
+ activity.mSceneTransitionInfo = r.mSceneTransitionInfo;
+ r.mSceneTransitionInfo = null;
}
activity.mLaunchedFromBubble = r.mLaunchedFromBubble;
activity.mCalled = false;
@@ -3962,7 +3964,7 @@
@Override
public void handleStartActivity(ActivityClientRecord r,
- PendingTransactionActions pendingActions, ActivityOptions activityOptions) {
+ PendingTransactionActions pendingActions, SceneTransitionInfo sceneTransitionInfo) {
final Activity activity = r.activity;
if (!r.stopped) {
throw new IllegalStateException("Can't start activity that is not stopped.");
@@ -3973,8 +3975,8 @@
}
unscheduleGcIdler();
- if (activityOptions != null) {
- activity.mPendingOptions = activityOptions;
+ if (sceneTransitionInfo != null) {
+ activity.mSceneTransitionInfo = sceneTransitionInfo;
}
// Start
@@ -4349,10 +4351,10 @@
}
}
- public void onNewActivityOptions(IBinder token, ActivityOptions options) {
+ public void onNewSceneTransitionInfo(IBinder token, SceneTransitionInfo info) {
ActivityClientRecord r = mActivities.get(token);
if (r != null) {
- r.activity.onNewActivityOptions(options);
+ r.activity.onNewSceneTransitionInfo(info);
}
}
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 6f4bb45..d947a9b 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -15,6 +15,7 @@
*/
package android.app;
+import android.app.ActivityOptions.SceneTransitionInfo;
import android.content.Intent;
import android.os.Bundle;
import android.os.ResultReceiver;
@@ -81,9 +82,9 @@
private EnterTransitionCoordinator mEnterTransitionCoordinator;
/**
- * ActivityOptions used on entering this Activity.
+ * {@link SceneTransitionInfo} used on entering this Activity.
*/
- private ActivityOptions mEnterActivityOptions;
+ private SceneTransitionInfo mEnterSceneTransitionInfo;
/**
* Has an exit transition been started? If so, we don't want to double-exit.
@@ -165,7 +166,7 @@
}
}
- public void setEnterActivityOptions(Activity activity, ActivityOptions options) {
+ public void setEnterSceneTransitionInfo(Activity activity, SceneTransitionInfo info) {
final Window window = activity.getWindow();
if (window == null) {
return;
@@ -173,16 +174,15 @@
// ensure Decor View has been created so that the window features are activated
window.getDecorView();
if (window.hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
- && options != null && mEnterActivityOptions == null
- && mEnterTransitionCoordinator == null
- && options.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
- mEnterActivityOptions = options;
+ && info != null && mEnterSceneTransitionInfo == null
+ && mEnterTransitionCoordinator == null) {
+ mEnterSceneTransitionInfo = info;
mIsEnterTriggered = false;
- if (mEnterActivityOptions.isReturning()) {
+ if (mEnterSceneTransitionInfo.isReturning()) {
restoreExitedViews();
- int result = mEnterActivityOptions.getResultCode();
+ int result = mEnterSceneTransitionInfo.getResultCode();
if (result != 0) {
- Intent intent = mEnterActivityOptions.getResultData();
+ Intent intent = mEnterSceneTransitionInfo.getResultData();
if (intent != null) {
intent.setExtrasClassLoader(activity.getClassLoader());
}
@@ -193,25 +193,26 @@
}
public void enterReady(Activity activity) {
- if (mEnterActivityOptions == null || mIsEnterTriggered) {
+ if (mEnterSceneTransitionInfo == null || mIsEnterTriggered) {
return;
}
mIsEnterTriggered = true;
mHasExited = false;
- ArrayList<String> sharedElementNames = mEnterActivityOptions.getSharedElementNames();
- ResultReceiver resultReceiver = mEnterActivityOptions.getResultReceiver();
- final boolean isReturning = mEnterActivityOptions.isReturning();
+ final ArrayList<String> sharedElementNames =
+ mEnterSceneTransitionInfo.getSharedElementNames();
+ ResultReceiver resultReceiver = mEnterSceneTransitionInfo.getResultReceiver();
+ final boolean isReturning = mEnterSceneTransitionInfo.isReturning();
if (isReturning) {
restoreExitedViews();
activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
}
getPendingExitNames(); // Set mPendingExitNames before resetting mEnterTransitionCoordinator
mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
- resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning(),
- mEnterActivityOptions.isCrossTask());
- if (mEnterActivityOptions.isCrossTask()) {
- mExitingFrom = new ArrayList<>(mEnterActivityOptions.getSharedElementNames());
- mExitingTo = new ArrayList<>(mEnterActivityOptions.getSharedElementNames());
+ resultReceiver, sharedElementNames, mEnterSceneTransitionInfo.isReturning(),
+ mEnterSceneTransitionInfo.isCrossTask());
+ if (mEnterSceneTransitionInfo.isCrossTask() && sharedElementNames != null) {
+ mExitingFrom = new ArrayList<>(sharedElementNames);
+ mExitingTo = new ArrayList<>(sharedElementNames);
}
if (!mIsEnterPostponed) {
@@ -248,7 +249,7 @@
mExitingFrom = null;
mExitingTo = null;
mExitingToView = null;
- mEnterActivityOptions = null;
+ mEnterSceneTransitionInfo = null;
}
public void onStop(Activity activity) {
@@ -296,7 +297,7 @@
mExitingToView = null;
mCalledExitCoordinator = null;
mEnterTransitionCoordinator = null;
- mEnterActivityOptions = null;
+ mEnterSceneTransitionInfo = null;
mExitTransitionCoordinators = null;
}
@@ -386,9 +387,10 @@
mExitTransitionCoordinators == null) {
return;
}
- ActivityOptions activityOptions = new ActivityOptions(options);
- if (activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
- int key = activityOptions.getExitCoordinatorKey();
+ final ActivityOptions activityOptions = new ActivityOptions(options);
+ final SceneTransitionInfo info = activityOptions.getSceneTransitionInfo();
+ if (info != null) {
+ int key = info.getExitCoordinatorKey();
int index = mExitTransitionCoordinators.indexOfKey(key);
if (index >= 0) {
mCalledExitCoordinator = mExitTransitionCoordinators.valueAt(index).get();
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 25075e9..b300674 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityOptions.SceneTransitionInfo;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.DestroyActivityItem;
@@ -207,7 +208,7 @@
/** Perform activity start. */
public abstract void handleStartActivity(@NonNull ActivityClientRecord r,
- PendingTransactionActions pendingActions, ActivityOptions activityOptions);
+ PendingTransactionActions pendingActions, SceneTransitionInfo sceneTransitionInfo);
/** Get package info. */
public abstract LoadedApk getPackageInfoNoCheck(ApplicationInfo ai);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 014ddd41..edeec77 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3481,24 +3481,13 @@
}
mResources = r;
- // only do this if the user already has more than one preferred locale
- if (android.content.res.Flags.defaultLocale()
- && r.getConfiguration().getLocales().size() > 1) {
- LocaleConfig lc;
- if (getUserId() < 0) {
- lc = LocaleConfig.fromContextIgnoringOverride(this);
- } else {
- // This is needed because the app might have locale config overrides that need to
- // be read from disk in order for resources to correctly choose which values to
- // load.
- StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads();
- try {
- lc = new LocaleConfig(this);
- } finally {
- StrictMode.setThreadPolicy(policy);
- }
+ if (r != null) {
+ // only do this if the user already has more than one preferred locale
+ if (android.content.res.Flags.defaultLocale()
+ && r.getConfiguration().getLocales().size() > 1) {
+ LocaleConfig lc = LocaleConfig.fromContextIgnoringOverride(this);
+ mResourcesManager.setLocaleConfig(lc);
}
- mResourcesManager.setLocaleConfig(lc);
}
}
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index a301c18..59e0e99 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -16,6 +16,7 @@
package android.app;
+import android.app.ActivityOptions.SceneTransitionInfo;
import android.app.ContentProviderHolder;
import android.app.IInstrumentationWatcher;
import android.app.IUiAutomationConnection;
@@ -114,7 +115,7 @@
void scheduleCreateBackupAgent(in ApplicationInfo app,
int backupMode, int userId, int operationType);
void scheduleDestroyBackupAgent(in ApplicationInfo app, int userId);
- void scheduleOnNewActivityOptions(IBinder token, in Bundle options);
+ void scheduleOnNewSceneTransitionInfo(IBinder token, in SceneTransitionInfo info);
void scheduleSuicide();
void dispatchPackageBroadcast(int cmd, in String[] packages);
void scheduleCrash(in String msg, int typeId, in Bundle extras);
diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
index 6b5f19a..1b19ecd 100644
--- a/core/java/android/app/LocalActivityManager.java
+++ b/core/java/android/app/LocalActivityManager.java
@@ -179,7 +179,7 @@
}
mActivityThread.handleStartActivity(clientRecord, pendingActions,
- null /* activityOptions */);
+ null /* sceneTransitionInfo */);
r.curState = STARTED;
if (desiredState == RESUMED) {
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index be420de..62db90f 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -744,20 +744,21 @@
params, userId, /* getCropped = */ true);
Trace.endSection();
- if (pfd != null) {
- try (InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
- ImageDecoder.Source src = ImageDecoder.createSource(is.readAllBytes());
- return ImageDecoder.decodeBitmap(src, ((decoder, info, source) -> {
- // Mutable and hardware config can't be set at the same time.
- decoder.setMutableRequired(!hardware);
- // Let's do color management
- if (cmProxy != null) {
- cmProxy.doColorManagement(decoder, info);
- }
- }));
- } catch (OutOfMemoryError | IOException e) {
- Log.w(TAG, "Can't decode file", e);
- }
+ if (pfd == null) {
+ return null;
+ }
+ try (InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+ ImageDecoder.Source src = ImageDecoder.createSource(context.getResources(), is);
+ return ImageDecoder.decodeBitmap(src, ((decoder, info, source) -> {
+ // Mutable and hardware config can't be set at the same time.
+ decoder.setMutableRequired(!hardware);
+ // Let's do color management
+ if (cmProxy != null) {
+ cmProxy.doColorManagement(decoder, info);
+ }
+ }));
+ } catch (OutOfMemoryError | IOException e) {
+ Log.w(TAG, "Can't decode file", e);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 1190bf6..4d53701 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -23,7 +23,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityClient;
-import android.app.ActivityOptions;
+import android.app.ActivityOptions.SceneTransitionInfo;
import android.app.ActivityThread;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
@@ -73,7 +73,7 @@
private PersistableBundle mPersistentState;
private List<ResultInfo> mPendingResults;
private List<ReferrerIntent> mPendingNewIntents;
- private ActivityOptions mActivityOptions;
+ private SceneTransitionInfo mSceneTransitionInfo;
private boolean mIsForward;
private ProfilerInfo mProfilerInfo;
private IBinder mAssistToken;
@@ -104,8 +104,8 @@
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
ActivityClientRecord r = new ActivityClientRecord(mActivityToken, mIntent, mIdent, mInfo,
mOverrideConfig, mReferrer, mVoiceInteractor, mState, mPersistentState,
- mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
- client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble,
+ mPendingResults, mPendingNewIntents, mSceneTransitionInfo, mIsForward,
+ mProfilerInfo, client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble,
mTaskFragmentToken);
client.handleLaunchActivity(r, pendingActions, mDeviceId, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -136,7 +136,7 @@
@Nullable IVoiceInteractor voiceInteractor, int procState, @Nullable Bundle state,
@Nullable PersistableBundle persistentState, @Nullable List<ResultInfo> pendingResults,
@Nullable List<ReferrerIntent> pendingNewIntents,
- @Nullable ActivityOptions activityOptions,
+ @Nullable SceneTransitionInfo sceneTransitionInfo,
boolean isForward, @Nullable ProfilerInfo profilerInfo, @NonNull IBinder assistToken,
@Nullable IActivityClientController activityClientController,
@NonNull IBinder shareableActivityToken, boolean launchedFromBubble,
@@ -152,7 +152,7 @@
persistentState != null ? new PersistableBundle(persistentState) : null,
pendingResults != null ? new ArrayList<>(pendingResults) : null,
pendingNewIntents != null ? new ArrayList<>(pendingNewIntents) : null,
- activityOptions, isForward,
+ sceneTransitionInfo, isForward,
profilerInfo != null ? new ProfilerInfo(profilerInfo) : null,
assistToken, activityClientController, shareableActivityToken,
launchedFromBubble, taskFragmentToken);
@@ -193,7 +193,7 @@
dest.writePersistableBundle(mPersistentState);
dest.writeTypedList(mPendingResults, flags);
dest.writeTypedList(mPendingNewIntents, flags);
- dest.writeBundle(mActivityOptions != null ? mActivityOptions.toBundle() : null);
+ dest.writeTypedObject(mSceneTransitionInfo, flags);
dest.writeBoolean(mIsForward);
dest.writeTypedObject(mProfilerInfo, flags);
dest.writeStrongBinder(mAssistToken);
@@ -213,7 +213,8 @@
in.readPersistableBundle(getClass().getClassLoader()),
in.createTypedArrayList(ResultInfo.CREATOR),
in.createTypedArrayList(ReferrerIntent.CREATOR),
- ActivityOptions.fromBundle(in.readBundle()), in.readBoolean(),
+ in.readTypedObject(SceneTransitionInfo.CREATOR),
+ in.readBoolean(),
in.readTypedObject(ProfilerInfo.CREATOR),
in.readStrongBinder(),
IActivityClientController.Stub.asInterface(in.readStrongBinder()),
@@ -253,7 +254,7 @@
&& areBundlesEqualRoughly(mPersistentState, other.mPersistentState)
&& Objects.equals(mPendingResults, other.mPendingResults)
&& Objects.equals(mPendingNewIntents, other.mPendingNewIntents)
- && (mActivityOptions == null) == (other.mActivityOptions == null)
+ && (mSceneTransitionInfo == null) == (other.mSceneTransitionInfo == null)
&& mIsForward == other.mIsForward
&& Objects.equals(mProfilerInfo, other.mProfilerInfo)
&& Objects.equals(mAssistToken, other.mAssistToken)
@@ -276,7 +277,7 @@
result = 31 * result + getRoughBundleHashCode(mPersistentState);
result = 31 * result + Objects.hashCode(mPendingResults);
result = 31 * result + Objects.hashCode(mPendingNewIntents);
- result = 31 * result + (mActivityOptions != null ? 1 : 0);
+ result = 31 * result + (mSceneTransitionInfo != null ? 1 : 0);
result = 31 * result + (mIsForward ? 1 : 0);
result = 31 * result + Objects.hashCode(mProfilerInfo);
result = 31 * result + Objects.hashCode(mAssistToken);
@@ -325,7 +326,7 @@
+ ",persistentState=" + mPersistentState
+ ",pendingResults=" + mPendingResults
+ ",pendingNewIntents=" + mPendingNewIntents
- + ",options=" + mActivityOptions
+ + ",sceneTransitionInfo=" + mSceneTransitionInfo
+ ",profilerInfo=" + mProfilerInfo
+ ",assistToken=" + mAssistToken
+ ",shareableActivityToken=" + mShareableActivityToken + "}";
@@ -340,7 +341,7 @@
int procState, @Nullable Bundle state, @Nullable PersistableBundle persistentState,
@Nullable List<ResultInfo> pendingResults,
@Nullable List<ReferrerIntent> pendingNewIntents,
- @Nullable ActivityOptions activityOptions, boolean isForward,
+ @Nullable SceneTransitionInfo sceneTransitionInfo, boolean isForward,
@Nullable ProfilerInfo profilerInfo, @Nullable IBinder assistToken,
@Nullable IActivityClientController activityClientController,
@Nullable IBinder shareableActivityToken, boolean launchedFromBubble,
@@ -359,7 +360,7 @@
instance.mPersistentState = persistentState;
instance.mPendingResults = pendingResults;
instance.mPendingNewIntents = pendingNewIntents;
- instance.mActivityOptions = activityOptions;
+ instance.mSceneTransitionInfo = sceneTransitionInfo;
instance.mIsForward = isForward;
instance.mProfilerInfo = profilerInfo;
instance.mAssistToken = assistToken;
diff --git a/core/java/android/app/servertransaction/StartActivityItem.java b/core/java/android/app/servertransaction/StartActivityItem.java
index 8b98b21..a0f93ce 100644
--- a/core/java/android/app/servertransaction/StartActivityItem.java
+++ b/core/java/android/app/servertransaction/StartActivityItem.java
@@ -20,7 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityOptions;
+import android.app.ActivityOptions.SceneTransitionInfo;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.os.IBinder;
@@ -35,13 +35,13 @@
private static final String TAG = "StartActivityItem";
- private ActivityOptions mActivityOptions;
+ private SceneTransitionInfo mSceneTransitionInfo;
@Override
public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r,
@NonNull PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "startActivityItem");
- client.handleStartActivity(r, pendingActions, mActivityOptions);
+ client.handleStartActivity(r, pendingActions, mSceneTransitionInfo);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -57,13 +57,13 @@
/** Obtain an instance initialized with provided params. */
@NonNull
public static StartActivityItem obtain(@NonNull IBinder activityToken,
- @Nullable ActivityOptions activityOptions) {
+ @Nullable SceneTransitionInfo sceneTransitionInfo) {
StartActivityItem instance = ObjectPool.obtain(StartActivityItem.class);
if (instance == null) {
instance = new StartActivityItem();
}
instance.setActivityToken(activityToken);
- instance.mActivityOptions = activityOptions;
+ instance.mSceneTransitionInfo = sceneTransitionInfo;
return instance;
}
@@ -71,7 +71,7 @@
@Override
public void recycle() {
super.recycle();
- mActivityOptions = null;
+ mSceneTransitionInfo = null;
ObjectPool.recycle(this);
}
@@ -81,13 +81,13 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
- dest.writeBundle(mActivityOptions != null ? mActivityOptions.toBundle() : null);
+ dest.writeTypedObject(mSceneTransitionInfo, flags);
}
/** Read from Parcel. */
private StartActivityItem(@NonNull Parcel in) {
super(in);
- mActivityOptions = ActivityOptions.fromBundle(in.readBundle());
+ mSceneTransitionInfo = in.readTypedObject(SceneTransitionInfo.CREATOR);
}
public static final @NonNull Creator<StartActivityItem> CREATOR = new Creator<>() {
@@ -109,21 +109,21 @@
return false;
}
final StartActivityItem other = (StartActivityItem) o;
- return (mActivityOptions == null) == (other.mActivityOptions == null);
+ return (mSceneTransitionInfo == null) == (other.mSceneTransitionInfo == null);
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + super.hashCode();
- result = 31 * result + (mActivityOptions != null ? 1 : 0);
+ result = 31 * result + (mSceneTransitionInfo != null ? 1 : 0);
return result;
}
@Override
public String toString() {
return "StartActivityItem{" + super.toString()
- + ",options=" + mActivityOptions + "}";
+ + ",sceneTransitionInfo=" + mSceneTransitionInfo + "}";
}
}
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 2e47fee..ba94077 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -324,7 +324,7 @@
break;
case ON_START:
mTransactionHandler.handleStartActivity(r, mPendingActions,
- null /* activityOptions */);
+ null /* sceneTransitionInfo */);
break;
case ON_RESUME:
mTransactionHandler.handleResumeActivity(r, false /* finalStateRequest */,
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index 161fa79..cdb92ac 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -458,6 +458,29 @@
mSystemDataSyncFlags = info.mSystemDataSyncFlags;
}
+ /**
+ * This builder is used specifically to create a new association to be restored to a device
+ * that is potentially using a different user ID from the backed-up device.
+ *
+ * @hide
+ */
+ public Builder(int id, int userId, @NonNull String packageName, AssociationInfo info) {
+ mId = id;
+ mUserId = userId;
+ mPackageName = packageName;
+ mTag = info.mTag;
+ mDeviceMacAddress = info.mDeviceMacAddress;
+ mDisplayName = info.mDisplayName;
+ mDeviceProfile = info.mDeviceProfile;
+ mAssociatedDevice = info.mAssociatedDevice;
+ mSelfManaged = info.mSelfManaged;
+ mNotifyOnDeviceNearby = info.mNotifyOnDeviceNearby;
+ mRevoked = info.mRevoked;
+ mTimeApprovedMs = info.mTimeApprovedMs;
+ mLastTimeConnectedMs = info.mLastTimeConnectedMs;
+ mSystemDataSyncFlags = info.mSystemDataSyncFlags;
+ }
+
/** @hide */
@FlaggedApi(Flags.FLAG_ASSOCIATION_TAG)
@TestApi
diff --git a/core/java/android/companion/datatransfer/PermissionSyncRequest.java b/core/java/android/companion/datatransfer/PermissionSyncRequest.java
index 973fca30..34865f0 100644
--- a/core/java/android/companion/datatransfer/PermissionSyncRequest.java
+++ b/core/java/android/companion/datatransfer/PermissionSyncRequest.java
@@ -48,6 +48,15 @@
}
/** @hide */
+ @Override
+ public PermissionSyncRequest copyWithNewId(int associationId) {
+ PermissionSyncRequest newRequest = new PermissionSyncRequest(associationId);
+ newRequest.mUserId = this.mUserId;
+ newRequest.mUserConsented = this.mUserConsented;
+ return newRequest;
+ }
+
+ /** @hide */
@NonNull
public static final Creator<PermissionSyncRequest> CREATOR =
new Creator<PermissionSyncRequest>() {
diff --git a/core/java/android/companion/datatransfer/SystemDataTransferRequest.java b/core/java/android/companion/datatransfer/SystemDataTransferRequest.java
index 38a553d..c3a2aa4 100644
--- a/core/java/android/companion/datatransfer/SystemDataTransferRequest.java
+++ b/core/java/android/companion/datatransfer/SystemDataTransferRequest.java
@@ -103,4 +103,15 @@
public int describeContents() {
return 0;
}
+
+ /**
+ * Creates a copy of itself with new association ID.
+ *
+ * This method must be implemented to ensure that backup-and-restore can correctly re-map
+ * the restored requests to the restored associations that can potentially have different
+ * IDs than what was originally backed up.
+ *
+ * @hide
+ */
+ public abstract SystemDataTransferRequest copyWithNewId(int associationId);
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7c52238..ee1d117b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4207,7 +4207,9 @@
* new state of quiet mode. This is only sent to registered receivers, not manifest receivers.
*
* <p>This broadcast is similar to {@link #ACTION_MANAGED_PROFILE_AVAILABLE} but functions as a
- * generic broadcast for all profile users.
+ * generic broadcast for all users of type {@link android.os.UserManager#isProfile()}}. In
+ * case of a managed profile, both {@link #ACTION_MANAGED_PROFILE_AVAILABLE} and
+ * {@link #ACTION_PROFILE_AVAILABLE} broadcasts are sent.
*/
@FlaggedApi(FLAG_ALLOW_PRIVATE_PROFILE)
public static final String ACTION_PROFILE_AVAILABLE =
@@ -4221,7 +4223,9 @@
* new state of quiet mode. This is only sent to registered receivers, not manifest receivers.
*
* <p>This broadcast is similar to {@link #ACTION_MANAGED_PROFILE_UNAVAILABLE} but functions as
- * a generic broadcast for all profile users.
+ * a generic broadcast for all users of type {@link android.os.UserManager#isProfile()}}. In
+ * case of a managed profile, both {@link #ACTION_MANAGED_PROFILE_UNAVAILABLE} and
+ * {@link #ACTION_PROFILE_UNAVAILABLE} broadcasts are sent.
*/
@FlaggedApi(FLAG_ALLOW_PRIVATE_PROFILE)
public static final String ACTION_PROFILE_UNAVAILABLE =
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 012b6c4..cdda12e 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -273,9 +273,6 @@
* to the <code>retailDemo</code> value of
* {@link android.R.attr#protectionLevel}.
*
- * @deprecated This flag has been replaced by the retail demo role and is a no-op since Android
- * V.
- *
* @hide
*/
@SystemApi
diff --git a/core/java/android/hardware/face/FaceAuthenticateOptions.java b/core/java/android/hardware/face/FaceAuthenticateOptions.java
index 1c6de04..518f902a 100644
--- a/core/java/android/hardware/face/FaceAuthenticateOptions.java
+++ b/core/java/android/hardware/face/FaceAuthenticateOptions.java
@@ -261,7 +261,7 @@
* The reason for this operation when requested by the system (sysui),
* otherwise AUTHENTICATE_REASON_UNKNOWN.
*
- * See frameworks/base/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
+ * See packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
* for more details about each reason.
*/
@DataClass.Generated.Member
@@ -524,7 +524,7 @@
* The reason for this operation when requested by the system (sysui),
* otherwise AUTHENTICATE_REASON_UNKNOWN.
*
- * See frameworks/base/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
+ * See packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
* for more details about each reason.
*/
@DataClass.Generated.Member
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 967a0cc..286cf28 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -95,4 +95,6 @@
void registerWlcStateListener(in INfcWlcStateListener listener);
void unregisterWlcStateListener(in INfcWlcStateListener listener);
WlcLDeviceInfo getWlcLDeviceInfo();
+
+ void updateDiscoveryTechnology(IBinder b, int pollFlags, int listenFlags);
}
diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl
index 191385a..f4b4604 100644
--- a/core/java/android/nfc/INfcCardEmulation.aidl
+++ b/core/java/android/nfc/INfcCardEmulation.aidl
@@ -43,4 +43,7 @@
ApduServiceInfo getPreferredPaymentService(int userHandle);
boolean setServiceEnabledForCategoryOther(int userHandle, in ComponentName app, boolean status);
boolean isDefaultPaymentRegistered();
+
+ boolean overrideRoutingTable(int userHandle, String protocol, String technology);
+ boolean recoverRoutingTable(int userHandle);
}
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 8d75cac..f03fc0a 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -112,6 +112,9 @@
Bundle readerModeExtras = null;
Binder token;
+ int mPollTech = NfcAdapter.FLAG_USE_ALL_TECH;
+ int mListenTech = NfcAdapter.FLAG_USE_ALL_TECH;
+
public NfcActivityState(Activity activity) {
if (activity.isDestroyed()) {
throw new IllegalStateException("activity is already destroyed");
@@ -132,6 +135,9 @@
readerModeFlags = 0;
readerModeExtras = null;
token = null;
+
+ mPollTech = NfcAdapter.FLAG_USE_ALL_TECH;
+ mListenTech = NfcAdapter.FLAG_USE_ALL_TECH;
}
@Override
public String toString() {
@@ -278,6 +284,9 @@
int readerModeFlags = 0;
Bundle readerModeExtras = null;
Binder token;
+ int pollTech;
+ int listenTech;
+
synchronized (NfcActivityManager.this) {
NfcActivityState state = findActivityState(activity);
if (DBG) Log.d(TAG, "onResume() for " + activity + " " + state);
@@ -286,9 +295,15 @@
token = state.token;
readerModeFlags = state.readerModeFlags;
readerModeExtras = state.readerModeExtras;
+
+ pollTech = state.mPollTech;
+ listenTech = state.mListenTech;
}
if (readerModeFlags != 0) {
setReaderMode(token, readerModeFlags, readerModeExtras);
+ } else if (listenTech != NfcAdapter.FLAG_USE_ALL_TECH
+ || pollTech != NfcAdapter.FLAG_USE_ALL_TECH) {
+ changeDiscoveryTech(token, pollTech, listenTech);
}
requestNfcServiceCallback();
}
@@ -298,6 +313,9 @@
public void onActivityPaused(Activity activity) {
boolean readerModeFlagsSet;
Binder token;
+ int pollTech;
+ int listenTech;
+
synchronized (NfcActivityManager.this) {
NfcActivityState state = findActivityState(activity);
if (DBG) Log.d(TAG, "onPause() for " + activity + " " + state);
@@ -305,10 +323,17 @@
state.resumed = false;
token = state.token;
readerModeFlagsSet = state.readerModeFlags != 0;
+
+ pollTech = state.mPollTech;
+ listenTech = state.mListenTech;
}
if (readerModeFlagsSet) {
// Restore default p2p modes
setReaderMode(token, 0, null);
+ } else if (listenTech != NfcAdapter.FLAG_USE_ALL_TECH
+ || pollTech != NfcAdapter.FLAG_USE_ALL_TECH) {
+ changeDiscoveryTech(token,
+ NfcAdapter.FLAG_USE_ALL_TECH, NfcAdapter.FLAG_USE_ALL_TECH);
}
}
@@ -333,4 +358,53 @@
}
}
+ /** setDiscoveryTechnology() implementation */
+ public void setDiscoveryTech(Activity activity, int pollTech, int listenTech) {
+ boolean isResumed;
+ Binder token;
+ boolean readerModeFlagsSet;
+ synchronized (NfcActivityManager.this) {
+ NfcActivityState state = getActivityState(activity);
+ readerModeFlagsSet = state.readerModeFlags != 0;
+ state.mListenTech = listenTech;
+ state.mPollTech = pollTech;
+ token = state.token;
+ isResumed = state.resumed;
+ }
+ if (!readerModeFlagsSet && isResumed) {
+ changeDiscoveryTech(token, pollTech, listenTech);
+ } else if (readerModeFlagsSet) {
+ throw new IllegalStateException("Cannot be used when the Reader Mode is enabled");
+ }
+ }
+
+ /** resetDiscoveryTechnology() implementation */
+ public void resetDiscoveryTech(Activity activity) {
+ boolean isResumed;
+ Binder token;
+ boolean readerModeFlagsSet;
+ synchronized (NfcActivityManager.this) {
+ NfcActivityState state = getActivityState(activity);
+ readerModeFlagsSet = state.readerModeFlags != 0;
+ state.mListenTech = NfcAdapter.FLAG_USE_ALL_TECH;
+ state.mPollTech = NfcAdapter.FLAG_USE_ALL_TECH;
+ token = state.token;
+ isResumed = state.resumed;
+ }
+ if (readerModeFlagsSet) {
+ disableReaderMode(activity);
+ } else if (isResumed) {
+ changeDiscoveryTech(token, NfcAdapter.FLAG_USE_ALL_TECH, NfcAdapter.FLAG_USE_ALL_TECH);
+ }
+
+ }
+
+ private void changeDiscoveryTech(Binder token, int pollTech, int listenTech) {
+ try {
+ NfcAdapter.sService.updateDiscoveryTechnology(token, pollTech, listenTech);
+ } catch (RemoteException e) {
+ mAdapter.attemptDeadServiceRecovery(e);
+ }
+ }
+
}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 21e23ae..5a40e42 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -333,6 +333,19 @@
*/
public static final int FLAG_READER_NFC_BARCODE = 0x10;
+ /** @hide */
+ @IntDef(flag = true, prefix = {"FLAG_READER_"}, value = {
+ FLAG_READER_KEEP,
+ FLAG_READER_DISABLE,
+ FLAG_READER_NFC_A,
+ FLAG_READER_NFC_B,
+ FLAG_READER_NFC_F,
+ FLAG_READER_NFC_V,
+ FLAG_READER_NFC_BARCODE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PollTechnology {}
+
/**
* Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
* <p>
@@ -360,6 +373,76 @@
public static final String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
/**
+ * Flag for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+ * <p>
+ * Setting this flag enables listening for Nfc-A technology.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+ public static final int FLAG_LISTEN_NFC_PASSIVE_A = 0x1;
+
+ /**
+ * Flag for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+ * <p>
+ * Setting this flag enables listening for Nfc-B technology.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+ public static final int FLAG_LISTEN_NFC_PASSIVE_B = 1 << 1;
+
+ /**
+ * Flag for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+ * <p>
+ * Setting this flag enables listening for Nfc-F technology.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+ public static final int FLAG_LISTEN_NFC_PASSIVE_F = 1 << 2;
+
+ /**
+ * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+ * <p>
+ * Setting this flag disables listening.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+ public static final int FLAG_LISTEN_DISABLE = 0x0;
+
+ /**
+ * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+ * <p>
+ * Setting this flag disables polling.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+ public static final int FLAG_READER_DISABLE = 0x0;
+
+ /**
+ * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+ * <p>
+ * Setting this flag makes listening to use current flags.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+ public static final int FLAG_LISTEN_KEEP = -1;
+
+ /**
+ * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+ * <p>
+ * Setting this flag makes polling to use current flags.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+ public static final int FLAG_READER_KEEP = -1;
+
+ /** @hide */
+ public static final int FLAG_USE_ALL_TECH = 0xff;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = {"FLAG_LISTEN_"}, value = {
+ FLAG_LISTEN_KEEP,
+ FLAG_LISTEN_DISABLE,
+ FLAG_LISTEN_NFC_PASSIVE_A,
+ FLAG_LISTEN_NFC_PASSIVE_B,
+ FLAG_LISTEN_NFC_PASSIVE_F
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ListenTechnology {}
+
+ /**
* @hide
* @removed
*/
@@ -437,12 +520,14 @@
@Retention(RetentionPolicy.SOURCE)
public @interface TagIntentAppPreferenceResult {}
- // Guarded by NfcAdapter.class
+ // Guarded by sLock
static boolean sIsInitialized = false;
static boolean sHasNfcFeature;
static boolean sHasCeFeature;
static boolean sHasNfcWlcFeature;
+ static Object sLock = new Object();
+
// Final after first constructor, except for
// attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
// recovery
@@ -1235,7 +1320,7 @@
@java.lang.Deprecated
@UnsupportedAppUsage
public void setBeamPushUris(Uri[] uris, Activity activity) {
- synchronized (NfcAdapter.class) {
+ synchronized (sLock) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
@@ -1305,7 +1390,7 @@
@java.lang.Deprecated
@UnsupportedAppUsage
public void setBeamPushUrisCallback(CreateBeamUrisCallback callback, Activity activity) {
- synchronized (NfcAdapter.class) {
+ synchronized (sLock) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
@@ -1390,7 +1475,7 @@
@UnsupportedAppUsage
public void setNdefPushMessage(NdefMessage message, Activity activity,
Activity ... activities) {
- synchronized (NfcAdapter.class) {
+ synchronized (sLock) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
@@ -1404,7 +1489,7 @@
@SystemApi
@UnsupportedAppUsage
public void setNdefPushMessage(NdefMessage message, Activity activity, int flags) {
- synchronized (NfcAdapter.class) {
+ synchronized (sLock) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
@@ -1483,7 +1568,7 @@
@UnsupportedAppUsage
public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
Activity ... activities) {
- synchronized (NfcAdapter.class) {
+ synchronized (sLock) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
@@ -1534,7 +1619,7 @@
@UnsupportedAppUsage
public void setOnNdefPushCompleteCallback(OnNdefPushCompleteCallback callback,
Activity activity, Activity ... activities) {
- synchronized (NfcAdapter.class) {
+ synchronized (sLock) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
@@ -1577,7 +1662,7 @@
*/
public void enableForegroundDispatch(Activity activity, PendingIntent intent,
IntentFilter[] filters, String[][] techLists) {
- synchronized (NfcAdapter.class) {
+ synchronized (sLock) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
@@ -1612,7 +1697,7 @@
* @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
*/
public void disableForegroundDispatch(Activity activity) {
- synchronized (NfcAdapter.class) {
+ synchronized (sLock) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
@@ -1648,7 +1733,7 @@
*/
public void enableReaderMode(Activity activity, ReaderCallback callback, int flags,
Bundle extras) {
- synchronized (NfcAdapter.class) {
+ synchronized (sLock) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
@@ -1665,7 +1750,7 @@
* @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
*/
public void disableReaderMode(Activity activity) {
- synchronized (NfcAdapter.class) {
+ synchronized (sLock) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
@@ -1693,7 +1778,7 @@
@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
@SuppressLint("VisiblySynchronized")
public void setReaderMode(boolean enablePolling) {
- synchronized (NfcAdapter.class) {
+ synchronized (sLock) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
@@ -1708,6 +1793,80 @@
}
/**
+ * Set the NFC controller to enable specific poll/listen technologies,
+ * as specified in parameters, while this Activity is in the foreground.
+ *
+ * Use {@link #FLAG_READER_KEEP} to keep current polling technology.
+ * Use {@link #FLAG_LISTEN_KEEP} to keep current listenig technology.
+ * Use {@link #FLAG_READER_DISABLE} to disable polling.
+ * Use {@link #FLAG_LISTEN_DISABLE} to disable listening.
+ * Also refer to {@link #resetDiscoveryTechnology(Activity)} to restore these changes.
+ * </p>
+ * The pollTech, listenTech parameters can be one or several of below list.
+ * <pre>
+ * Poll Listen
+ * Passive A 0x01 (NFC_A) 0x01 (NFC_PASSIVE_A)
+ * Passive B 0x02 (NFC_B) 0x02 (NFC_PASSIVE_B)
+ * Passive F 0x04 (NFC_F) 0x04 (NFC_PASSIVE_F)
+ * ISO 15693 0x08 (NFC_V) -
+ * Kovio 0x10 (NFC_BARCODE) -
+ * </pre>
+ * <p>Example usage in an Activity that requires to disable poll,
+ * keep current listen technologies:
+ * <pre>
+ * protected void onResume() {
+ * mNfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
+ * mNfcAdapter.setDiscoveryTechnology(this,
+ * NfcAdapter.FLAG_READER_DISABLE, NfcAdapter.FLAG_LISTEN_KEEP);
+ * }</pre></p>
+ * @param activity The Activity that requests NFC controller to enable specific technologies.
+ * @param pollTech Flags indicating poll technologies.
+ * @param listenTech Flags indicating listen technologies.
+ * @throws UnsupportedOperationException if FEATURE_NFC,
+ * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF are unavailable.
+ */
+
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+ public void setDiscoveryTechnology(@NonNull Activity activity,
+ @PollTechnology int pollTech, @ListenTechnology int listenTech) {
+ if (listenTech == FLAG_LISTEN_DISABLE) {
+ synchronized (sLock) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
+ mNfcActivityManager.enableReaderMode(activity, null, pollTech, null);
+ return;
+ }
+ if (pollTech == FLAG_READER_DISABLE) {
+ synchronized (sLock) {
+ if (!sHasCeFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
+ } else {
+ synchronized (sLock) {
+ if (!sHasNfcFeature || !sHasCeFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+ mNfcActivityManager.setDiscoveryTech(activity, pollTech, listenTech);
+ }
+
+ /**
+ * Restore the poll/listen technologies of NFC controller,
+ * which were changed by {@link #setDiscoveryTechnology(Activity , int , int)}
+ *
+ * @param activity The Activity that requests to changed technologies.
+ */
+
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+ public void resetDiscoveryTechnology(@NonNull Activity activity) {
+ mNfcActivityManager.resetDiscoveryTech(activity);
+ }
+
+ /**
* Manually invoke Android Beam to share data.
*
* <p>The Android Beam animation is normally only shown when two NFC-capable
@@ -1737,7 +1896,7 @@
@java.lang.Deprecated
@UnsupportedAppUsage
public boolean invokeBeam(Activity activity) {
- synchronized (NfcAdapter.class) {
+ synchronized (sLock) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
@@ -1775,7 +1934,7 @@
@Deprecated
@UnsupportedAppUsage
public void enableForegroundNdefPush(Activity activity, NdefMessage message) {
- synchronized (NfcAdapter.class) {
+ synchronized (sLock) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
@@ -1805,7 +1964,7 @@
@Deprecated
@UnsupportedAppUsage
public void disableForegroundNdefPush(Activity activity) {
- synchronized (NfcAdapter.class) {
+ synchronized (sLock) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
@@ -2085,7 +2244,7 @@
@java.lang.Deprecated
@UnsupportedAppUsage
public boolean isNdefPushEnabled() {
- synchronized (NfcAdapter.class) {
+ synchronized (sLock) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
@@ -2199,7 +2358,7 @@
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
public boolean addNfcUnlockHandler(final NfcUnlockHandler unlockHandler,
String[] tagTechnologies) {
- synchronized (NfcAdapter.class) {
+ synchronized (sLock) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
@@ -2248,7 +2407,7 @@
@SystemApi
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
public boolean removeNfcUnlockHandler(NfcUnlockHandler unlockHandler) {
- synchronized (NfcAdapter.class) {
+ synchronized (sLock) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 58b6179..ad86d70 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -998,6 +998,87 @@
}
}
+ /**
+ * Setting NFC controller routing table, which includes Protocol Route and Technology Route,
+ * while this Activity is in the foreground.
+ *
+ * The parameter set to null can be used to keep current values for that entry.
+ * <p>
+ * Example usage in an Activity that requires to set proto route to "ESE" and keep tech route:
+ * <pre>
+ * protected void onResume() {
+ * mNfcAdapter.overrideRoutingTable(this , "ESE" , null);
+ * }</pre>
+ * </p>
+ * Also activities must call this method when it goes to the background,
+ * with all parameters set to null.
+ * @param activity The Activity that requests NFC controller routing table to be changed.
+ * @param protocol ISO-DEP route destination, which can be "DH" or "UICC" or "ESE".
+ * @param technology Tech-A, Tech-B route destination, which can be "DH" or "UICC" or "ESE".
+ * @return true if operation is successful and false otherwise
+ *
+ * This is a high risk API and only included to support mainline effort
+ * @hide
+ */
+ public boolean overrideRoutingTable(Activity activity, String protocol, String technology) {
+ if (activity == null) {
+ throw new NullPointerException("activity or service or category is null");
+ }
+ if (!activity.isResumed()) {
+ throw new IllegalArgumentException("Activity must be resumed.");
+ }
+ try {
+ return sService.overrideRoutingTable(UserHandle.myUserId(), protocol, technology);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.overrideRoutingTable(UserHandle.myUserId(), protocol, technology);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Restore the NFC controller routing table,
+ * which was changed by {@link #overrideRoutingTable(Activity, String, String)}
+ *
+ * @param activity The Activity that requested NFC controller routing table to be changed.
+ * @return true if operation is successful and false otherwise
+ *
+ * @hide
+ */
+ public boolean recoverRoutingTable(Activity activity) {
+ if (activity == null) {
+ throw new NullPointerException("activity is null");
+ }
+ if (!activity.isResumed()) {
+ throw new IllegalArgumentException("Activity must be resumed.");
+ }
+ try {
+ return sService.recoverRoutingTable(UserHandle.myUserId());
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.recoverRoutingTable(UserHandle.myUserId());
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
void recoverService() {
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
sService = adapter.getCardEmulationService();
diff --git a/core/java/android/nfc/flags.aconfig b/core/java/android/nfc/flags.aconfig
index ce4f777..01a4570 100644
--- a/core/java/android/nfc/flags.aconfig
+++ b/core/java/android/nfc/flags.aconfig
@@ -62,3 +62,10 @@
description: "Flag for NFC charging changes"
bug: "292143899"
}
+
+flag {
+ name: "enable_nfc_set_discovery_tech"
+ namespace: "nfc"
+ description: "Flag for NFC set discovery tech API"
+ bug: "300351519"
+}
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index f71c269..07f7690 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -18,8 +18,6 @@
import static android.view.Display.DEFAULT_DISPLAY;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -47,11 +45,8 @@
import android.util.Log;
import android.view.Display;
-import libcore.io.Streams;
-
import java.io.ByteArrayInputStream;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
@@ -75,7 +70,6 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
-import java.util.zip.ZipInputStream;
import sun.security.pkcs.PKCS7;
import sun.security.pkcs.SignerInfo;
@@ -426,72 +420,43 @@
} finally {
raf.close();
}
-
- // Additionally verify the package compatibility.
- if (!readAndVerifyPackageCompatibilityEntry(packageFile)) {
- throw new SignatureException("package compatibility verification failed");
- }
}
/**
* Verifies the compatibility entry from an {@link InputStream}.
*
- * @return the verification result.
+ * @param inputStream The stream that contains the package compatibility info.
+ * @throws IOException Never.
+ * @return {@code true}.
+ * @deprecated This function no longer checks {@code inputStream} and
+ * unconditionally returns true. Instead, check compatibility when the
+ * OTA package is generated.
*/
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(
+ publicAlternatives = "Use {@code true} directly",
+ maxTargetSdk = Build.VERSION_CODES.VANILLA_ICE_CREAM)
private static boolean verifyPackageCompatibility(InputStream inputStream) throws IOException {
- ArrayList<String> list = new ArrayList<>();
- ZipInputStream zis = new ZipInputStream(inputStream);
- ZipEntry entry;
- while ((entry = zis.getNextEntry()) != null) {
- long entrySize = entry.getSize();
- if (entrySize > Integer.MAX_VALUE || entrySize < 0) {
- throw new IOException(
- "invalid entry size (" + entrySize + ") in the compatibility file");
- }
- byte[] bytes = new byte[(int) entrySize];
- Streams.readFully(zis, bytes);
- list.add(new String(bytes, UTF_8));
- }
- if (list.isEmpty()) {
- throw new IOException("no entries found in the compatibility file");
- }
- return (VintfObject.verify(list.toArray(new String[list.size()])) == 0);
- }
-
- /**
- * Reads and verifies the compatibility entry in an OTA zip package. The compatibility entry is
- * a zip file (inside the OTA package zip).
- *
- * @return {@code true} if the entry doesn't exist or verification passes.
- */
- private static boolean readAndVerifyPackageCompatibilityEntry(File packageFile)
- throws IOException {
- try (ZipFile zip = new ZipFile(packageFile)) {
- ZipEntry entry = zip.getEntry("compatibility.zip");
- if (entry == null) {
- return true;
- }
- InputStream inputStream = zip.getInputStream(entry);
- return verifyPackageCompatibility(inputStream);
- }
+ return true;
}
/**
* Verifies the package compatibility info against the current system.
*
* @param compatibilityFile the {@link File} that contains the package compatibility info.
- * @throws IOException if there were any errors reading the compatibility file.
- * @return the compatibility verification result.
+ * @throws IOException Never.
+ * @return {@code true}
+ * @deprecated This function no longer checks {@code compatibilityFile} and
+ * unconditionally returns true. Instead, check compatibility when the
+ * OTA package is generated.
*
* {@hide}
*/
+ @Deprecated
@SystemApi
@SuppressLint("RequiresPermission")
public static boolean verifyPackageCompatibility(File compatibilityFile) throws IOException {
- try (InputStream inputStream = new FileInputStream(compatibilityFile)) {
- return verifyPackageCompatibility(inputStream);
- }
+ return true;
}
/**
diff --git a/core/java/android/os/UpdateEngineStable.java b/core/java/android/os/UpdateEngineStable.java
new file mode 100644
index 0000000..9e2593e
--- /dev/null
+++ b/core/java/android/os/UpdateEngineStable.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.IntDef;
+
+/**
+ * UpdateEngineStable handles calls to the update engine stalbe which takes care of A/B OTA updates.
+ * This interface has lesser functionalities than UpdateEngine and doesn't allow cancel.
+ *
+ * <p>The minimal flow is:
+ *
+ * <ol>
+ * <li>Create a new UpdateEngineStable instance.
+ * <li>Call {@link #bind}, provide callback function.
+ * <li>Call {@link #applyPayloadFd}.
+ * </ol>
+ *
+ * The APIs defined in this class and UpdateEngineStableCallback class must be in sync with the ones
+ * in {@code system/update_engine/stable/android/os/IUpdateEngineStable.aidl} and {@code
+ * ssystem/update_engine/stable/android/os/IUpdateEngineStableCallback.aidl}.
+ *
+ * @hide
+ */
+public class UpdateEngineStable {
+ private static final String TAG = "UpdateEngineStable";
+
+ private static final String UPDATE_ENGINE_STABLE_SERVICE =
+ "android.os.UpdateEngineStableService";
+
+ /**
+ * Error codes from update engine upon finishing a call to {@link applyPayloadFd}. Values will
+ * be passed via the callback function {@link
+ * UpdateEngineStableCallback#onPayloadApplicationComplete}. Values must agree with the ones in
+ * {@code system/update_engine/common/error_code.h}.
+ */
+ /** @hide */
+ @IntDef(
+ value = {
+ UpdateEngine.ErrorCodeConstants.SUCCESS,
+ UpdateEngine.ErrorCodeConstants.ERROR,
+ UpdateEngine.ErrorCodeConstants.FILESYSTEM_COPIER_ERROR,
+ UpdateEngine.ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR,
+ UpdateEngine.ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR,
+ UpdateEngine.ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR,
+ UpdateEngine.ErrorCodeConstants.KERNEL_DEVICE_OPEN_ERROR,
+ UpdateEngine.ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR,
+ UpdateEngine.ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR,
+ UpdateEngine.ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR,
+ UpdateEngine.ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR,
+ UpdateEngine.ErrorCodeConstants.PAYLOAD_TIMESTAMP_ERROR,
+ UpdateEngine.ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE,
+ UpdateEngine.ErrorCodeConstants.NOT_ENOUGH_SPACE,
+ UpdateEngine.ErrorCodeConstants.DEVICE_CORRUPTED,
+ })
+ public @interface ErrorCode {}
+
+ private final IUpdateEngineStable mUpdateEngineStable;
+ private IUpdateEngineStableCallback mUpdateEngineStableCallback = null;
+ private final Object mUpdateEngineStableCallbackLock = new Object();
+
+ /**
+ * Creates a new instance.
+ *
+ * @hide
+ */
+ public UpdateEngineStable() {
+ mUpdateEngineStable =
+ IUpdateEngineStable.Stub.asInterface(
+ ServiceManager.getService(UPDATE_ENGINE_STABLE_SERVICE));
+ if (mUpdateEngineStable == null) {
+ throw new IllegalStateException("Failed to find " + UPDATE_ENGINE_STABLE_SERVICE);
+ }
+ }
+
+ /**
+ * Prepares this instance for use. The callback will be notified on any status change, and when
+ * the update completes. A handler can be supplied to control which thread runs the callback, or
+ * null.
+ *
+ * @hide
+ */
+ public boolean bind(final UpdateEngineStableCallback callback, final Handler handler) {
+ synchronized (mUpdateEngineStableCallbackLock) {
+ mUpdateEngineStableCallback =
+ new IUpdateEngineStableCallback.Stub() {
+ @Override
+ public void onStatusUpdate(final int status, final float percent) {
+ if (handler != null) {
+ handler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ callback.onStatusUpdate(status, percent);
+ }
+ });
+ } else {
+ callback.onStatusUpdate(status, percent);
+ }
+ }
+
+ @Override
+ public void onPayloadApplicationComplete(final int errorCode) {
+ if (handler != null) {
+ handler.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ callback.onPayloadApplicationComplete(errorCode);
+ }
+ });
+ } else {
+ callback.onPayloadApplicationComplete(errorCode);
+ }
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return super.VERSION;
+ }
+
+ @Override
+ public String getInterfaceHash() {
+ return super.HASH;
+ }
+ };
+
+ try {
+ return mUpdateEngineStable.bind(mUpdateEngineStableCallback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Equivalent to {@code bind(callback, null)}.
+ *
+ * @hide
+ */
+ public boolean bind(final UpdateEngineStableCallback callback) {
+ return bind(callback, null);
+ }
+
+ /**
+ * Applies payload from given ParcelFileDescriptor. Usage is same as UpdateEngine#applyPayload
+ *
+ * @hide
+ */
+ public void applyPayloadFd(
+ ParcelFileDescriptor fd, long offset, long size, String[] headerKeyValuePairs) {
+ try {
+ mUpdateEngineStable.applyPayloadFd(fd, offset, size, headerKeyValuePairs);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unbinds the last bound callback function.
+ *
+ * @hide
+ */
+ public boolean unbind() {
+ synchronized (mUpdateEngineStableCallbackLock) {
+ if (mUpdateEngineStableCallback == null) {
+ return true;
+ }
+ try {
+ boolean result = mUpdateEngineStable.unbind(mUpdateEngineStableCallback);
+ mUpdateEngineStableCallback = null;
+ return result;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+}
diff --git a/core/java/android/os/UpdateEngineStableCallback.java b/core/java/android/os/UpdateEngineStableCallback.java
new file mode 100644
index 0000000..4bcfb4b
--- /dev/null
+++ b/core/java/android/os/UpdateEngineStableCallback.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * Callback function for UpdateEngineStable. Used to keep the caller up to date with progress, so
+ * the UI (if any) can be updated.
+ *
+ * <p>The APIs defined in this class and UpdateEngineStable class must be in sync with the ones in
+ * system/update_engine/stable/android/os/IUpdateEngineStable.aidl and
+ * system/update_engine/stable/android/os/IUpdateEngineStableCallback.aidl.
+ *
+ * <p>{@hide}
+ */
+public abstract class UpdateEngineStableCallback {
+
+ /**
+ * Invoked when anything changes. The value of {@code status} will be one of the values from
+ * {@link UpdateEngine.UpdateStatusConstants}, and {@code percent} will be valid
+ *
+ * @hide
+ */
+ public abstract void onStatusUpdate(int status, float percent);
+
+ /**
+ * Invoked when the payload has been applied, whether successfully or unsuccessfully. The value
+ * of {@code errorCode} will be one of the values from {@link UpdateEngine.ErrorCodeConstants}.
+ *
+ * @hide
+ */
+ public abstract void onPayloadApplicationComplete(@UpdateEngineStable.ErrorCode int errorCode);
+}
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index d09c229..fae7cb3 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -64,3 +64,10 @@
description: "enable Permission PREPARE_FACTORY_RESET."
bug: "302016478"
}
+
+flag {
+ name: "retail_demo_role_enabled"
+ namespace: "permissions"
+ description: "default retail demo role holder"
+ bug: "274132354"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 54cc5f4..84fddcb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7428,6 +7428,20 @@
public static final String DEFAULT_INPUT_METHOD = "default_input_method";
/**
+ * Used only by {@link com.android.server.inputmethod.InputMethodManagerService} as a
+ * temporary data store of {@link #DEFAULT_INPUT_METHOD} while a virtual-device-specific
+ * input method is set as default.</p>
+ *
+ * <p>This should be considered to be an implementation detail of
+ * {@link com.android.server.inputmethod.InputMethodManagerService}. Other system
+ * components should never rely on this value.</p>
+ *
+ * @see #DEFAULT_INPUT_METHOD
+ * @hide
+ */
+ public static final String DEFAULT_DEVICE_INPUT_METHOD = "default_device_input_method";
+
+ /**
* Setting to record the input method subtype used by default, holding the ID
* of the desired method.
*/
@@ -14347,6 +14361,19 @@
"mute_alarm_stream_with_ringer_mode";
/**
+ * The user's choice for whether or not Alarm stream should always be muted with Ringer.
+ *
+ * <p>Note that this is different from {@link #MUTE_ALARM_STREAM_WITH_RINGER_MODE}, which
+ * controls the real state of whether or not the Alarm stream and Ringer association occurs.
+ * The two Settings are not necessarily equal, if the final decision for the association
+ * depends on factors beyond the user's preference.
+ *
+ * @hide
+ */
+ public static final String MUTE_ALARM_STREAM_WITH_RINGER_MODE_USER_PREFERENCE =
+ "mute_alarm_stream_with_ringer_mode_user_preference";
+
+ /**
* Overlay display devices setting.
* The associated value is a specially formatted string that describes the
* size and density of simulated secondary display devices.
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index e4af2da..d47ff2e 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -368,11 +368,13 @@
* <p>
* As of Android 11 apps will need specific permission to query other packages. To use
* this method an app must include in their AndroidManifest:
+ * <pre>{@code
* <queries>
* <intent>
* <action android:name="android.provider.Telephony.SMS_DELIVER"/>
* </intent>
* </queries>
+ * }</pre>
* Which will allow them to query packages which declare intent filters that include
* the {@link android.provider.Telephony.Sms.Intents#SMS_DELIVER_ACTION} intent.
* </p>
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 692dad4..1a2be15 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -1439,12 +1439,12 @@
// to make sure the wallpaper is stopped even after the events
// onSurfaceCreated() and onSurfaceChanged().
if (noConsecutiveVisibilityEvents()) {
- if (DEBUG) Log.v(TAG, "toggling doVisibilityChanged");
- Trace.beginSection("WPMS.Engine.doVisibilityChanged-true");
- doVisibilityChanged(true);
+ if (DEBUG) Log.v(TAG, "toggling onVisibilityChanged");
+ Trace.beginSection("WPMS.Engine.onVisibilityChanged-true");
+ onVisibilityChanged(true);
Trace.endSection();
- Trace.beginSection("WPMS.Engine.doVisibilityChanged-false");
- doVisibilityChanged(false);
+ Trace.beginSection("WPMS.Engine.onVisibilityChanged-false");
+ onVisibilityChanged(false);
Trace.endSection();
} else {
if (DEBUG) {
diff --git a/core/java/android/view/DisplayAddress.java b/core/java/android/view/DisplayAddress.java
index 99e811a..c3c4ab5 100644
--- a/core/java/android/view/DisplayAddress.java
+++ b/core/java/android/view/DisplayAddress.java
@@ -138,6 +138,30 @@
out.writeLong(mPhysicalDisplayId);
}
+ /**
+ * This method is meant to check to see if the ports match
+ * @param a1 Address to compare
+ * @param a2 Address to compare
+ *
+ * @return true if the arguments have the same port, and at least one does not specify
+ * a model.
+ */
+ public static boolean isPortMatch(DisplayAddress a1, DisplayAddress a2) {
+ // Both displays must be of type Physical
+ if (!(a1 instanceof Physical && a2 instanceof Physical)) {
+ return false;
+ }
+ Physical p1 = (Physical) a1;
+ Physical p2 = (Physical) a2;
+
+ // If both addresses specify a model, fallback to a basic match check (which
+ // also checks the port).
+ if (p1.getModel() != null && p2.getModel() != null) {
+ return p1.equals(p2);
+ }
+ return p1.getPort() == p2.getPort();
+ }
+
private Physical(long physicalDisplayId) {
mPhysicalDisplayId = physicalDisplayId;
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 61cf126..f61ed51 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3163,6 +3163,12 @@
public static final int PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS = 1 << 10;
/**
+ * Flag to indicate that the window is forcibly to go edge-to-edge.
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED = 1 << 11;
+
+ /**
* Flag to indicate that the window frame should be the requested frame adding the display
* cutout frame. This will only be applied if a specific size smaller than the parent frame
* is given, and the window is covering the display cutout. The extended frame will not be
@@ -3338,6 +3344,7 @@
PRIVATE_FLAG_SYSTEM_ERROR,
PRIVATE_FLAG_OPTIMIZE_MEASURE,
PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS,
+ PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED,
PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT,
PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY,
PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME,
@@ -3400,6 +3407,10 @@
equals = PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS,
name = "DISABLE_WALLPAPER_TOUCH_EVENTS"),
@ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED,
+ equals = PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED,
+ name = "EDGE_TO_EDGE_ENFORCED"),
+ @ViewDebug.FlagToString(
mask = PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT,
equals = PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT,
name = "LAYOUT_SIZE_EXTENDED_BY_CUTOUT"),
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index 0cc19fb..48f8f1b 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -24,6 +24,13 @@
}
flag {
+ namespace: "accessibility"
+ name: "braille_display_hid"
+ description: "Enables new APIs for an AccessibilityService to communicate with a HID Braille display"
+ bug: "303522222"
+}
+
+flag {
name: "cleanup_accessibility_warning_dialog"
namespace: "accessibility"
description: "Cleans up duplicated or broken logic surrounding the accessibility warning dialog."
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 216acdc..3366a7e 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -38,6 +38,14 @@
}
flag {
+ name: "draw_magnifier_border_outside_wmlock"
+ namespace: "windowing_frontend"
+ description: "Avoid holding WM locks for a long time when executing lockCanvas"
+ bug: "316075123"
+ is_fixed_read_only: true
+}
+
+flag {
name: "introduce_smoother_dimmer"
namespace: "windowing_frontend"
description: "Refactor dim to fix flickers"
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index 36b7ee5..d62c8f3 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -130,4 +130,10 @@
* Note that it's called only if the device is interactive.
*/
void onSystemKeyPressed(int keycode);
+
+ /**
+ * Requests to show the keyguard immediately without locking the device. Keyguard will show
+ * whether a screen lock was configured or not (including if screen lock is SWIPE or NONE).
+ */
+ void showDismissibleKeyguard();
}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 31910ac..54fdcc6 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -31,6 +31,7 @@
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.PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
@@ -294,9 +295,9 @@
private int mFrameResource = 0;
private int mTextColor = 0;
- int mStatusBarColor = 0;
- int mNavigationBarColor = 0;
- int mNavigationBarDividerColor = 0;
+ int mStatusBarColor = Color.TRANSPARENT;
+ int mNavigationBarColor = Color.TRANSPARENT;
+ int mNavigationBarDividerColor = Color.TRANSPARENT;
private boolean mForcedStatusBarColor = false;
private boolean mForcedNavigationBarColor = false;
@@ -393,6 +394,7 @@
|| (CompatChanges.isChangeEnabled(ENFORCE_EDGE_TO_EDGE)
&& Flags.enforceEdgeToEdge());
if (mEdgeToEdgeEnforced) {
+ getAttributes().privateFlags |= PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED;
mDecorFitsSystemWindows = false;
}
}
@@ -2548,17 +2550,10 @@
final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;
final boolean targetPreQ = targetSdk < Build.VERSION_CODES.Q;
- if (!mForcedStatusBarColor) {
- final int statusBarCompatibleColor = context.getColor(R.color.status_bar_compatible);
- final int statusBarDefaultColor = context.getColor(R.color.status_bar_default);
- final int statusBarColor = a.getColor(R.styleable.Window_statusBarColor,
- statusBarDefaultColor);
-
- mStatusBarColor = statusBarColor == statusBarDefaultColor && !mEdgeToEdgeEnforced
- ? statusBarCompatibleColor
- : statusBarColor;
+ if (!mForcedStatusBarColor && !mEdgeToEdgeEnforced) {
+ mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, Color.BLACK);
}
- if (!mForcedNavigationBarColor) {
+ if (!mForcedNavigationBarColor && !mEdgeToEdgeEnforced) {
final int navBarCompatibleColor = context.getColor(R.color.navigation_bar_compatible);
final int navBarDefaultColor = context.getColor(R.color.navigation_bar_default);
final int navBarColor = a.getColor(R.styleable.Window_navigationBarColor,
@@ -2566,7 +2561,6 @@
mNavigationBarColor =
navBarColor == navBarDefaultColor
- && !mEdgeToEdgeEnforced
&& !context.getResources().getBoolean(
R.bool.config_navBarDefaultTransparent)
? navBarCompatibleColor
@@ -2575,7 +2569,7 @@
mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor,
Color.TRANSPARENT);
}
- if (!targetPreQ) {
+ if (!targetPreQ && !mEdgeToEdgeEnforced) {
mEnsureStatusBarContrastWhenTransparent = a.getBoolean(
R.styleable.Window_enforceStatusBarContrast, false);
mEnsureNavigationBarContrastWhenTransparent = a.getBoolean(
@@ -3899,6 +3893,9 @@
@Override
public void setStatusBarColor(int color) {
+ if (mEdgeToEdgeEnforced) {
+ return;
+ }
if (mStatusBarColor == color && mForcedStatusBarColor) {
return;
}
@@ -3920,6 +3917,9 @@
@Override
public void setNavigationBarColor(int color) {
+ if (mEdgeToEdgeEnforced) {
+ return;
+ }
if (mNavigationBarColor == color && mForcedNavigationBarColor) {
return;
}
@@ -3936,6 +3936,9 @@
@Override
public void setNavigationBarDividerColor(int navigationBarDividerColor) {
+ if (mEdgeToEdgeEnforced) {
+ return;
+ }
mNavigationBarDividerColor = navigationBarDividerColor;
if (mDecor != null) {
mDecor.updateColorViews(null, false /* animate */);
@@ -3949,6 +3952,9 @@
@Override
public void setStatusBarContrastEnforced(boolean ensureContrast) {
+ if (mEdgeToEdgeEnforced) {
+ return;
+ }
mEnsureStatusBarContrastWhenTransparent = ensureContrast;
if (mDecor != null) {
mDecor.updateColorViews(null, false /* animate */);
@@ -3962,6 +3968,9 @@
@Override
public void setNavigationBarContrastEnforced(boolean enforceContrast) {
+ if (mEdgeToEdgeEnforced) {
+ return;
+ }
mEnsureNavigationBarContrastWhenTransparent = enforceContrast;
if (mDecor != null) {
mDecor.updateColorViews(null, false /* animate */);
@@ -4031,6 +4040,9 @@
@Override
public void setDecorFitsSystemWindows(boolean decorFitsSystemWindows) {
+ if (mEdgeToEdgeEnforced) {
+ return;
+ }
mDecorFitsSystemWindows = decorFitsSystemWindows;
applyDecorFitsSystemWindows();
}
diff --git a/core/jni/hwbinder/EphemeralStorage.cpp b/core/jni/hwbinder/EphemeralStorage.cpp
index 95bb42e..ef0750c 100644
--- a/core/jni/hwbinder/EphemeralStorage.cpp
+++ b/core/jni/hwbinder/EphemeralStorage.cpp
@@ -164,7 +164,7 @@
}
default:
- CHECK(!"Should not be here");
+ CHECK(!"Should not be here") << "Item type: " << item.mType;
}
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e65bfab..1eeffb9 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1709,6 +1709,7 @@
<!-- @SystemApi Allows camera access by Headless System User 0 when device is running in
HSUM Mode.
+ @FlaggedApi("com.android.internal.camera.flags.camera_hsum_permission")
@hide -->
<permission android:name="android.permission.CAMERA_HEADLESS_SYSTEM_USER"
android:permissionGroup="android.permission-group.UNDEFINED"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index d1143c4..a23201e 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -301,9 +301,7 @@
granted to the system companion device manager service -->
<flag name="companion" value="0x800000" />
<!-- Additional flag from base permission type: this permission will be granted to the
- retail demo app, as defined by the OEM.
- This flag has been replaced by the retail demo role and is a no-op since Android V.
- -->
+ retail demo app, as defined by the OEM. -->
<flag name="retailDemo" value="0x1000000" />
<!-- Additional flag from base permission type: this permission will be granted to the
recents app. -->
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index eddd81e..53a6270 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -568,10 +568,6 @@
<color name="side_fps_button_color">#00677E</color>
<!-- Color for system bars -->
- <color name="status_bar_compatible">@android:color/black</color>
- <!-- This uses non-regular transparent intentionally. It is used to tell if the transparent
- color is set by the framework or not. -->
- <color name="status_bar_default">#00808080</color>
<color name="navigation_bar_compatible">@android:color/black</color>
<!-- This uses non-regular transparent intentionally. It is used to tell if the transparent
color is set by the framework or not. -->
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 540967d..17bb86a 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -128,7 +128,7 @@
</staging-public-group>
<staging-public-group type="string" first-id="0x01ba0000">
- <!-- @hide @SystemApi -->
+ <!-- @hide @SystemApi @FlaggedApi("android.permission.flags.retail_demo_role_enabled") -->
<public name="config_defaultRetailDemo" />
</staging-public-group>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b0a4c16..d12ef2b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3095,8 +3095,6 @@
<java-symbol type="bool" name="config_navBarDefaultTransparent" />
<java-symbol type="color" name="navigation_bar_default"/>
<java-symbol type="color" name="navigation_bar_compatible"/>
- <java-symbol type="color" name="status_bar_default"/>
- <java-symbol type="color" name="status_bar_compatible"/>
<!-- EditText suggestion popup. -->
<java-symbol type="id" name="suggestionWindowContainer" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index d5d67ab..bdbf96b 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -190,7 +190,7 @@
<item name="windowTranslucentStatus">false</item>
<item name="windowTranslucentNavigation">false</item>
<item name="windowDrawsSystemBarBackgrounds">false</item>
- <item name="statusBarColor">@color/status_bar_default</item>
+ <item name="statusBarColor">@color/black</item>
<item name="navigationBarColor">@color/navigation_bar_default</item>
<item name="windowActionBarFullscreenDecorLayout">@layout/screen_action_bar</item>
<item name="windowContentTransitions">false</item>
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 723c081..a796a0f 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -171,7 +171,8 @@
@Test
public void testRecycleStartActivityItem() {
- testRecycle(() -> StartActivityItem.obtain(mActivityToken, ActivityOptions.makeBasic()));
+ testRecycle(() -> StartActivityItem.obtain(mActivityToken,
+ new ActivityOptions.SceneTransitionInfo()));
}
@Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
index c0e2a49..3823033 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
@@ -255,9 +255,9 @@
return LaunchActivityItem.obtain(mActivityToken, mIntent, mIdent, mInfo,
mCurConfig, mOverrideConfig, mDeviceId, mReferrer, mVoiceInteractor,
mProcState, mState, mPersistentState, mPendingResults, mPendingNewIntents,
- mActivityOptions, mIsForward, mProfilerInfo, mAssistToken,
- null /* activityClientController */, mShareableActivityToken,
- mLaunchedFromBubble, mTaskFragmentToken);
+ mActivityOptions != null ? mActivityOptions.getSceneTransitionInfo() : null,
+ mIsForward, mProfilerInfo, mAssistToken, null /* activityClientController */,
+ mShareableActivityToken, mLaunchedFromBubble, mTaskFragmentToken);
}
}
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 07921bf..952cdd9 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -262,7 +262,7 @@
public void testStart() {
// Write to parcel
StartActivityItem item = StartActivityItem.obtain(mActivityToken,
- ActivityOptions.makeBasic());
+ new ActivityOptions.SceneTransitionInfo());
writeAndPrepareForReading(item);
// Read from parcel and assert
diff --git a/data/keyboards/Android.bp b/data/keyboards/Android.bp
new file mode 100644
index 0000000..f15c153
--- /dev/null
+++ b/data/keyboards/Android.bp
@@ -0,0 +1,29 @@
+// Copyright 2010 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.
+
+genrule {
+ name: "validate_framework_keymaps",
+ srcs: [
+ "*.kl",
+ "*.kcm",
+ "*.idc",
+ ],
+ tools: ["validatekeymaps"],
+ out: ["stamp"],
+ cmd: "$(location validatekeymaps) -q $(in) " +
+ "&& touch $(out)",
+ dist: {
+ targets: ["droidcore"],
+ },
+}
diff --git a/data/keyboards/Android.mk b/data/keyboards/Android.mk
deleted file mode 100644
index 6ae8800..0000000
--- a/data/keyboards/Android.mk
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright (C) 2010 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.
-
-# This makefile performs build time validation of framework keymap files.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(LOCAL_PATH)/common.mk
-
-# Validate all key maps.
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := validate_framework_keymaps
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-intermediates := $(call intermediates-dir-for,ETC,$(LOCAL_MODULE),,COMMON)
-LOCAL_BUILT_MODULE := $(intermediates)/stamp
-
-validatekeymaps := $(HOST_OUT_EXECUTABLES)/validatekeymaps$(HOST_EXECUTABLE_SUFFIX)
-$(LOCAL_BUILT_MODULE): PRIVATE_VALIDATEKEYMAPS := $(validatekeymaps)
-$(LOCAL_BUILT_MODULE) : $(framework_keylayouts) $(framework_keycharmaps) $(framework_keyconfigs) | $(validatekeymaps)
- $(hide) $(PRIVATE_VALIDATEKEYMAPS) -q $^
- $(hide) mkdir -p $(dir $@) && touch $@
-
-# Run validatekeymaps uncondionally for platform build.
-droidcore : $(LOCAL_BUILT_MODULE)
-
-# Reset temp vars.
-validatekeymaps :=
-framework_keylayouts :=
-framework_keycharmaps :=
-framework_keyconfigs :=
diff --git a/data/keyboards/common.mk b/data/keyboards/common.mk
deleted file mode 100644
index d75b691..0000000
--- a/data/keyboards/common.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright (C) 2010 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.
-
-# This is the list of framework provided keylayouts and key character maps to include.
-# Used by Android.mk and keyboards.mk.
-
-framework_keylayouts := $(wildcard $(LOCAL_PATH)/*.kl)
-
-framework_keycharmaps := $(wildcard $(LOCAL_PATH)/*.kcm)
-
-framework_keyconfigs := $(wildcard $(LOCAL_PATH)/*.idc)
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 65597de..066f38b 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -819,14 +819,20 @@
Log.e(TAG, "onTaskFragmentParentInfoChanged on empty Task id=" + taskId);
return;
}
+ // Checks if container should be updated before apply new parentInfo.
+ final boolean shouldUpdateContainer = taskContainer.shouldUpdateContainer(parentInfo);
taskContainer.updateTaskFragmentParentInfo(parentInfo);
if (!taskContainer.isVisible()) {
// Don't update containers if the task is not visible. We only update containers when
// parentInfo#isVisibleRequested is true.
return;
}
- if (isInPictureInPicture(parentInfo.getConfiguration())) {
- // No need to update presentation in PIP until the Task exit PIP.
+
+ // If the last direct activity of the host task is dismissed and the overlay container is
+ // the only taskFragment, the overlay container should also be dismissed.
+ dismissOverlayContainerIfNeeded(wct, taskContainer);
+
+ if (!shouldUpdateContainer) {
return;
}
updateContainersInTask(wct, taskContainer);
@@ -1947,11 +1953,8 @@
void updateOverlayContainer(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container) {
final TaskContainer taskContainer = container.getTaskContainer();
- // Dismiss the overlay container if it's the only container in the task and there's no
- // direct activity in the parent task.
- if (taskContainer.getTaskFragmentContainers().size() == 1
- && !taskContainer.hasDirectActivity()) {
- container.finish(false /* shouldFinishDependent */, mPresenter, wct, this);
+
+ if (dismissOverlayContainerIfNeeded(wct, taskContainer)) {
return;
}
@@ -1968,6 +1971,24 @@
}
}
+ /** Dismisses the overlay container in the {@code taskContainer} if needed. */
+ @GuardedBy("mLock")
+ private boolean dismissOverlayContainerIfNeeded(@NonNull WindowContainerTransaction wct,
+ @NonNull TaskContainer taskContainer) {
+ final TaskFragmentContainer overlayContainer = taskContainer.getOverlayContainer();
+ if (overlayContainer == null) {
+ return false;
+ }
+ // Dismiss the overlay container if it's the only container in the task and there's no
+ // direct activity in the parent task.
+ if (taskContainer.getTaskFragmentContainers().size() == 1
+ && !taskContainer.hasDirectActivity()) {
+ mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */);
+ return true;
+ }
+ return false;
+ }
+
/**
* Updates {@link SplitContainer} with the given {@link SplitAttributes} if the
* {@link SplitContainer} is the top most and not finished. If passed {@link SplitAttributes}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 64ad4fa..71195b6 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -138,6 +138,21 @@
}
/**
+ * Returns {@code true} if the container should be updated with {@code info}.
+ */
+ boolean shouldUpdateContainer(@NonNull TaskFragmentParentInfo info) {
+ final Configuration configuration = info.getConfiguration();
+
+ return info.isVisible()
+ // No need to update presentation in PIP until the Task exit PIP.
+ && !isInPictureInPicture(configuration)
+ // If the task properties equals regardless of starting position, don't need to
+ // update the container.
+ && (mConfiguration.diffPublicOnly(configuration) != 0
+ || mDisplayId != info.getDisplayId());
+ }
+
+ /**
* Returns the windowing mode for the TaskFragments below this Task, which should be split with
* other TaskFragments.
*
@@ -161,7 +176,11 @@
}
boolean isInPictureInPicture() {
- return getWindowingMode() == WINDOWING_MODE_PINNED;
+ return isInPictureInPicture(mConfiguration);
+ }
+
+ private static boolean isInPictureInPicture(@NonNull Configuration configuration) {
+ return configuration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED;
}
boolean isInMultiWindow() {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index b52971a..6fe8e50 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -616,6 +616,9 @@
* Removes all activities that belong to this process and finishes other containers/activities
* configured to finish together.
*/
+ // Suppress GuardedBy warning because lint ask to mark this method as
+ // @GuardedBy(container.mController.mLock), which is mLock itself
+ @SuppressWarnings("GuardedBy")
@GuardedBy("mController.mLock")
void finish(boolean shouldFinishDependent, @NonNull SplitPresenter presenter,
@NonNull WindowContainerTransaction wct, @NonNull SplitController controller) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java
index 396956e..6624c70 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java
@@ -77,9 +77,11 @@
@NonNull
TransactionRecord startNewTransaction(@Nullable IBinder taskFragmentTransactionToken) {
if (mCurrentTransaction != null) {
+ final TransactionRecord lastTransaction = mCurrentTransaction;
mCurrentTransaction = null;
throw new IllegalStateException(
- "The previous transaction has not been applied or aborted,");
+ "The previous transaction:" + lastTransaction + " has not been applied or "
+ + "aborted.");
}
mCurrentTransaction = new TransactionRecord(taskFragmentTransactionToken);
return mCurrentTransaction;
@@ -199,5 +201,15 @@
? mOriginType
: TASK_FRAGMENT_TRANSIT_CHANGE;
}
+
+ @Override
+ @NonNull
+ public String toString() {
+ return TransactionRecord.class.getSimpleName() + "{"
+ + "token=" + mTaskFragmentTransactionToken
+ + ", type=" + getTransactionTransitionType()
+ + ", transaction=" + mTransaction
+ + "}";
+ }
}
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 5ef6a52..bc92101 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -472,6 +472,29 @@
verify(mSplitPresenter).applyActivityStackAttributes(any(), eq(container), eq(attrs));
}
+ @Test
+ public void testOnTaskFragmentParentInfoChanged_positionOnlyChange_earlyReturn() {
+ final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test");
+
+ final TaskContainer taskContainer = overlayContainer.getTaskContainer();
+ spyOn(taskContainer);
+ final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties();
+ final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(
+ new Configuration(taskProperties.getConfiguration()), taskProperties.getDisplayId(),
+ true /* visible */, false /* hasDirectActivity */, null /* decorSurface */);
+ parentInfo.getConfiguration().windowConfiguration.getBounds().offset(10, 10);
+
+ mSplitController.onTaskFragmentParentInfoChanged(mTransaction, TASK_ID, parentInfo);
+
+ // The parent info must be applied to the task container
+ verify(taskContainer).updateTaskFragmentParentInfo(parentInfo);
+ verify(mSplitController, never()).updateContainer(any(), any());
+
+ assertWithMessage("The overlay container must still be dismissed even if "
+ + "#updateContainer is not called")
+ .that(taskContainer.getOverlayContainer()).isNull();
+ }
+
/**
* A simplified version of {@link SplitController.ActivityStartMonitor
* #createOrUpdateOverlayTaskFragmentIfNeeded}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
index 6213f62..8f04f12 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
@@ -63,6 +63,10 @@
if ((touchX < mStartThresholdX && mSwipeEdge == BackEvent.EDGE_LEFT)
|| (touchX > mStartThresholdX && mSwipeEdge == BackEvent.EDGE_RIGHT)) {
mStartThresholdX = touchX;
+ if ((mSwipeEdge == BackEvent.EDGE_LEFT && mStartThresholdX < mInitTouchX)
+ || (mSwipeEdge == BackEvent.EDGE_RIGHT && mStartThresholdX > mInitTouchX)) {
+ mInitTouchX = mStartThresholdX;
+ }
}
mLatestTouchX = touchX;
mLatestTouchY = touchY;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index 34c015f..84f21f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -330,7 +330,7 @@
continue;
}
if (isHide) {
- if (pending.mType == TRANSIT_TO_BACK) {
+ if (pending != null && pending.mType == TRANSIT_TO_BACK) {
// TO_BACK is only used when setting the task view visibility immediately,
// so in that case we can also hide the surface immediately
startTransaction.hide(chg.getLeash());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt
index bf07dcc..6dbb1e2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt
@@ -170,6 +170,71 @@
nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / nonLinearTarget)
}
+ @Test
+ fun restartingGesture_resetsInitialTouchX_leftEdge() {
+ val linearTracker = linearTouchTracker()
+ linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
+ var touchX = 100f
+ val velocityX = 0f
+ val velocityY = 0f
+
+ // assert that progress is increased when increasing touchX
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE)
+
+ // assert that progress is reset to 0 when start location is updated
+ linearTracker.updateStartLocation()
+ linearTracker.assertProgress(0f)
+
+ // assert that progress remains 0 when touchX is decreased
+ touchX -= 50
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress(0f)
+
+ // assert that progress uses new minimal touchX for progress calculation
+ val newInitialTouchX = touchX
+ touchX += 100
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress((touchX - newInitialTouchX) / MAX_DISTANCE)
+
+ // assert the same for triggerBack==true
+ linearTracker.triggerBack = true
+ linearTracker.assertProgress((touchX - newInitialTouchX) / MAX_DISTANCE)
+ }
+
+ @Test
+ fun restartingGesture_resetsInitialTouchX_rightEdge() {
+ val linearTracker = linearTouchTracker()
+ linearTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0f, BackEvent.EDGE_RIGHT)
+
+ var touchX = INITIAL_X_RIGHT_EDGE - 100f
+ val velocityX = 0f
+ val velocityY = 0f
+
+ // assert that progress is increased when decreasing touchX
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / MAX_DISTANCE)
+
+ // assert that progress is reset to 0 when start location is updated
+ linearTracker.updateStartLocation()
+ linearTracker.assertProgress(0f)
+
+ // assert that progress remains 0 when touchX is increased
+ touchX += 50
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress(0f)
+
+ // assert that progress uses new maximal touchX for progress calculation
+ val newInitialTouchX = touchX
+ touchX -= 100
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress((newInitialTouchX - touchX) / MAX_DISTANCE)
+
+ // assert the same for triggerBack==true
+ linearTracker.triggerBack = true
+ linearTracker.assertProgress((newInitialTouchX - touchX) / MAX_DISTANCE)
+ }
+
companion object {
private const val MAX_DISTANCE = 500f
private const val LINEAR_DISTANCE = 400f
diff --git a/media/java/android/media/LoudnessCodecConfigurator.java b/media/java/android/media/LoudnessCodecConfigurator.java
index de9d87c0..aadd783 100644
--- a/media/java/android/media/LoudnessCodecConfigurator.java
+++ b/media/java/android/media/LoudnessCodecConfigurator.java
@@ -234,19 +234,21 @@
* @param mediaCodec the codec to start receiving asynchronous loudness
* updates. The codec has to be in a configured or started
* state in order to add it for loudness updates.
- * @throws IllegalArgumentException if the {@code mediaCodec} was not configured,
- * does not contain loudness metadata or if it
- * was already added before
+ * @throws IllegalArgumentException if the same {@code mediaCodec} was already
+ * added before.
+ * @return {@code false} if the {@code mediaCodec} was not configured or does
+ * not contain loudness metadata, {@code true} otherwise.
*/
@FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
- public void addMediaCodec(@NonNull MediaCodec mediaCodec) {
+ public boolean addMediaCodec(@NonNull MediaCodec mediaCodec) {
final MediaCodec mc = Objects.requireNonNull(mediaCodec,
"MediaCodec for addMediaCodec cannot be null");
int piid = PLAYER_PIID_INVALID;
final LoudnessCodecInfo mcInfo = getCodecInfo(mc);
if (mcInfo == null) {
- throw new IllegalArgumentException("Could not extract codec loudness information");
+ Log.v(TAG, "Could not extract codec loudness information");
+ return false;
}
synchronized (mConfiguratorLock) {
final AtomicBoolean containsCodec = new AtomicBoolean(false);
@@ -271,6 +273,8 @@
if (piid != PLAYER_PIID_INVALID) {
mLcDispatcher.addLoudnessCodecInfo(piid, mediaCodec.hashCode(), mcInfo);
}
+
+ return true;
}
/**
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index b7c97208..470a8ac 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -3401,13 +3401,15 @@
}
/**
- * Set a harware graphic buffer to this queue request. Exactly one buffer must
+ * Set a hardware graphic buffer to this queue request. Exactly one buffer must
* be set for a queue request before calling {@link #queue}.
* <p>
* Note: buffers should have format {@link HardwareBuffer#YCBCR_420_888},
* a single layer, and an appropriate usage ({@link HardwareBuffer#USAGE_CPU_READ_OFTEN}
* for software codecs and {@link HardwareBuffer#USAGE_VIDEO_ENCODE} for hardware)
- * for codecs to recognize. Codecs may throw exception if the buffer is not recognizable.
+ * for codecs to recognize. Format {@link ImageFormat#PRIVATE} together with
+ * usage {@link HardwareBuffer#USAGE_VIDEO_ENCODE} will also work for hardware codecs.
+ * Codecs may throw exception if the buffer is not recognizable.
*
* @param buffer The hardware graphic buffer object
* @return this object
diff --git a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
index ce1004c..74e5612 100644
--- a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
+++ b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
@@ -19,6 +19,7 @@
import static android.media.audio.Flags.FLAG_LOUDNESS_CONFIGURATOR_API;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -191,6 +192,18 @@
@Test
@RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
+ public void addUnconfiguredMediaCodec_returnsFalse() throws Exception {
+ final MediaCodec mediaCodec = MediaCodec.createDecoderByType("audio/mpeg");
+
+ try {
+ assertFalse(mLcc.addMediaCodec(mediaCodec));
+ } finally {
+ mediaCodec.release();
+ }
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
public void setClearTrack_removeAllAudioServicePiidCodecs() throws Exception {
final ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
final AudioTrack track = createAudioTrack();
diff --git a/packages/CredentialManager/res/drawable/more_horiz_24px.xml b/packages/CredentialManager/res/drawable/more_horiz_24px.xml
new file mode 100644
index 0000000..7b235f8
--- /dev/null
+++ b/packages/CredentialManager/res/drawable/more_horiz_24px.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M240,560Q207,560 183.5,536.5Q160,513 160,480Q160,447 183.5,423.5Q207,400 240,400Q273,400 296.5,423.5Q320,447 320,480Q320,513 296.5,536.5Q273,560 240,560ZM480,560Q447,560 423.5,536.5Q400,513 400,480Q400,447 423.5,423.5Q447,400 480,400Q513,400 536.5,423.5Q560,447 560,480Q560,513 536.5,536.5Q513,560 480,560ZM720,560Q687,560 663.5,536.5Q640,513 640,480Q640,447 663.5,423.5Q687,400 720,400Q753,400 776.5,423.5Q800,447 800,480Q800,513 776.5,536.5Q753,560 720,560Z"/>
+</vector>
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index dfa5735..8ac364e7 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -305,6 +305,14 @@
var i = 0
var datasetAdded = false
+ val duplicateDisplayNames: MutableMap<String, Boolean> = mutableMapOf()
+ providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach {
+ val credentialEntry = it.sortedCredentialEntryList.first()
+ credentialEntry.displayName?.let {displayName ->
+ val duplicateEntry = duplicateDisplayNames.contains(displayName)
+ duplicateDisplayNames[displayName] = duplicateEntry
+ }
+ }
providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@{
val primaryEntry = it.sortedCredentialEntryList.first()
val pendingIntent = primaryEntry.pendingIntent
@@ -339,10 +347,14 @@
} else {
spec = inlinePresentationSpecs[inlinePresentationSpecsCount - 1]
}
+ val displayName : String = primaryEntry.displayName ?: primaryEntry.userName
val sliceBuilder = InlineSuggestionUi
.newContentBuilder(pendingIntent)
- .setTitle(primaryEntry.userName)
+ .setTitle(displayName)
sliceBuilder.setStartIcon(icon)
+ if (duplicateDisplayNames[displayName] == true) {
+ sliceBuilder.setSubtitle(primaryEntry.userName)
+ }
inlinePresentation = InlinePresentation(
sliceBuilder.build().slice, spec, /* pinned= */ false)
}
@@ -398,7 +410,7 @@
val sliceBuilder = InlineSuggestionUi
.newContentBuilder(bottomSheetPendingIntent)
.setStartIcon(Icon.createWithResource(this,
- com.android.credentialmanager.R.drawable.ic_other_sign_in_24))
+ com.android.credentialmanager.R.drawable.more_horiz_24px))
val presentationBuilder = Presentations.Builder()
.setInlinePresentation(InlinePresentation(
sliceBuilder.build().slice, spec, /* pinned= */ true))
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java
index 754437e..b5af845 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java
@@ -76,7 +76,7 @@
boolean hasRequestInstallPermission = Arrays.asList(getRequestedPermissions(callingPackage))
.contains(permission.REQUEST_INSTALL_PACKAGES);
boolean hasInstallPermission = getBaseContext().checkPermission(permission.INSTALL_PACKAGES,
- 0 /* random value for pid */, callingUid) != PackageManager.PERMISSION_GRANTED;
+ 0 /* random value for pid */, callingUid) == PackageManager.PERMISSION_GRANTED;
if (!hasRequestInstallPermission && !hasInstallPermission) {
Log.e(TAG, "Uid " + callingUid + " does not have "
+ permission.REQUEST_INSTALL_PACKAGES + " or "
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java
index 6ccbc4c..42dd382 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java
@@ -28,12 +28,13 @@
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
String appTitle = getArguments().getString(UnarchiveActivity.APP_TITLE);
+ String installerTitle = getArguments().getString(UnarchiveActivity.INSTALLER_TITLE);
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity());
dialogBuilder.setTitle(
String.format(getContext().getString(R.string.unarchive_application_title),
- appTitle));
+ appTitle, installerTitle));
dialogBuilder.setMessage(R.string.unarchive_body_text);
dialogBuilder.setPositiveButton(R.string.restore, this);
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index f4edb36..460a6f7 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -24,7 +24,8 @@
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
import com.android.settingslib.spa.gallery.card.CardPageProvider
import com.android.settingslib.spa.gallery.chart.ChartPageProvider
-import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
+import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider
+import com.android.settingslib.spa.gallery.dialog.NavDialogProvider
import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuBoxPageProvider
import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuCheckBoxProvider
@@ -91,7 +92,8 @@
ProgressBarPageProvider,
LoadingBarPageProvider,
ChartPageProvider,
- AlertDialogPageProvider,
+ DialogMainPageProvider,
+ NavDialogProvider,
ItemListPageProvider,
ItemOperatePageProvider,
OperateListPageProvider,
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/DialogMainPageProvider.kt
similarity index 84%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/DialogMainPageProvider.kt
index 1545a3e..4e3fcee 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/DialogMainPageProvider.kt
@@ -28,10 +28,10 @@
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
-private const val TITLE = "AlertDialogPage"
+private const val TITLE = "Category: Dialog"
-object AlertDialogPageProvider : SettingsPageProvider {
- override val name = "AlertDialogPage"
+object DialogMainPageProvider : SettingsPageProvider {
+ override val name = "DialogMain"
private val owner = createSettingsPage()
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> = listOf(
@@ -47,6 +47,12 @@
override val onClick = alertDialogPresenter::open
})
}.build(),
+ SettingsEntryBuilder.create("NavDialog", owner).setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = "Navigate to Dialog"
+ override val onClick = navigator(route = NavDialogProvider.name)
+ })
+ }.build(),
)
fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/NavDialogProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/NavDialogProvider.kt
new file mode 100644
index 0000000..6f79911
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/NavDialogProvider.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.gallery.dialog
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.widget.dialog.SettingsDialogCard
+
+object NavDialogProvider : SettingsPageProvider {
+ override val name = "NavDialog"
+ override val navType = SettingsPageProvider.NavType.Dialog
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ SettingsDialogCard("Example Nav Dialog") {}
+ }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
index 6a2e598..1f028d5 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
@@ -30,7 +30,7 @@
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
import com.android.settingslib.spa.gallery.card.CardPageProvider
import com.android.settingslib.spa.gallery.chart.ChartPageProvider
-import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
+import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider
import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
import com.android.settingslib.spa.gallery.itemList.OperateListPageProvider
import com.android.settingslib.spa.gallery.page.ArgumentPageModel
@@ -71,7 +71,7 @@
ProgressBarPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
LoadingBarPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
ChartPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
- AlertDialogPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+ DialogMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
EditorMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
CardPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
CopyablePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index 9f8c868..5605485 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -22,7 +22,6 @@
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.annotation.VisibleForTesting
-import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
@@ -30,15 +29,19 @@
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.core.view.WindowCompat
+import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavGraph.Companion.findStartDestination
+import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
+import androidx.navigation.compose.dialog
import androidx.navigation.compose.rememberNavController
import com.android.settingslib.spa.R
import com.android.settingslib.spa.framework.common.LogCategory
import com.android.settingslib.spa.framework.common.NullPageProvider
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.SettingsPageProvider.NavType
import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.common.createSettingsPage
@@ -127,27 +130,31 @@
allProvider: Collection<SettingsPageProvider>,
content: @Composable (SettingsPage) -> Unit,
) {
- // TODO(b/298520326): Remove Box after the issue is fixed.
- // Wrap the top level node into a Box to workaround an issue of Compose 1.6.0-alpha03.
- Box {
- NavHost(
- navController = navController,
- startDestination = NullPageProvider.name,
- ) {
- composable(NullPageProvider.name) {}
- for (spp in allProvider) {
- animatedComposable(
- route = spp.name + spp.parameter.navRoute(),
- arguments = spp.parameter,
- ) { navBackStackEntry ->
- val page = remember { spp.createSettingsPage(navBackStackEntry.arguments) }
- content(page)
- }
+ NavHost(
+ navController = navController,
+ startDestination = NullPageProvider.name,
+ ) {
+ composable(NullPageProvider.name) {}
+ for (spp in allProvider) {
+ destination(spp) { navBackStackEntry ->
+ val page = remember { spp.createSettingsPage(navBackStackEntry.arguments) }
+ content(page)
}
}
}
}
+private fun NavGraphBuilder.destination(
+ spp: SettingsPageProvider,
+ content: @Composable (NavBackStackEntry) -> Unit,
+) {
+ val route = spp.name + spp.parameter.navRoute()
+ when (spp.navType) {
+ NavType.Page -> animatedComposable(route, spp.parameter) { content(it) }
+ NavType.Dialog -> dialog(route, spp.parameter) { content(it) }
+ }
+}
+
@Composable
private fun NavControllerWrapperImpl.InitialDestination(
initialIntent: Intent?,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
index 18f964e..81bee5e 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
@@ -34,6 +34,14 @@
/** The page provider name, needs to be *unique* and *stable*. */
val name: String
+ enum class NavType {
+ Page,
+ Dialog,
+ }
+
+ val navType: NavType
+ get() = NavType.Page
+
/** The display name of this page provider, for better readability. */
val displayName: String
get() = name
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt
index 8b172da..f08e740 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt
@@ -19,10 +19,13 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.AlertDialogDefaults
import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.window.Dialog
+import androidx.navigation.compose.NavHost
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsShape
import com.android.settingslib.spa.widget.ui.SettingsTitle
@@ -34,13 +37,27 @@
content: @Composable () -> Unit,
) {
Dialog(onDismissRequest = onDismissRequest) {
- Card(shape = SettingsShape.CornerExtraLarge) {
- Column(modifier = Modifier.padding(vertical = SettingsDimension.itemPaddingAround)) {
- Box(modifier = Modifier.padding(SettingsDimension.dialogItemPadding)) {
- SettingsTitle(title = title, useMediumWeight = true)
- }
- content()
+ SettingsDialogCard(title, content)
+ }
+}
+
+/**
+ * Card for dialog, suitable for independent dialog in the [NavHost].
+ */
+@Composable
+fun SettingsDialogCard(
+ title: String,
+ content: @Composable () -> Unit,
+) {
+ Card(
+ shape = SettingsShape.CornerExtraLarge,
+ colors = CardDefaults.cardColors(containerColor = AlertDialogDefaults.containerColor),
+ ) {
+ Column(modifier = Modifier.padding(vertical = SettingsDimension.itemPaddingAround)) {
+ Box(modifier = Modifier.padding(SettingsDimension.dialogItemPadding)) {
+ SettingsTitle(title = title, useMediumWeight = true)
}
+ content()
}
}
}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt
index 92d3411..8cbd964 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt
@@ -18,6 +18,7 @@
import android.content.Context
import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onNodeWithText
@@ -29,12 +30,14 @@
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.tests.testutils.SppDialog
import com.android.settingslib.spa.tests.testutils.SpaEnvironmentForTest
import com.android.settingslib.spa.tests.testutils.SpaLoggerForTest
import com.android.settingslib.spa.tests.testutils.SppDisabled
import com.android.settingslib.spa.tests.testutils.SppHome
import com.android.settingslib.spa.testutils.waitUntil
-import com.google.common.truth.Truth
+import com.android.settingslib.spa.testutils.waitUntilExists
+import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -106,11 +109,31 @@
composeTestRule.onNodeWithText(sppDisabled.getTitle(null)).assertDoesNotExist()
spaLogger.verifyPageEvent(pageDisabled.id, 0, 0)
}
+
+ @Test
+ fun browseContent_dialog() {
+ val spaEnvironment = SpaEnvironmentForTest(
+ context = context,
+ rootPages = listOf(SppHome.createSettingsPage()),
+ logger = spaLogger,
+ )
+ SpaEnvironmentFactory.reset(spaEnvironment)
+ val sppRepository by spaEnvironment.pageProviderRepository
+
+ composeTestRule.setContent {
+ BrowseContent(
+ sppRepository = sppRepository,
+ isPageEnabled = SettingsPage::isEnabled,
+ initialIntent = null,
+ )
+ }
+ composeTestRule.onNodeWithText(SppDialog.name).performClick()
+
+ composeTestRule.waitUntilExists(hasText(SppDialog.CONTENT))
+ }
}
private fun SpaLoggerForTest.verifyPageEvent(id: String, entryCount: Int, leaveCount: Int) {
- Truth.assertThat(getEventCount(id, LogEvent.PAGE_ENTER, LogCategory.FRAMEWORK))
- .isEqualTo(entryCount)
- Truth.assertThat(getEventCount(id, LogEvent.PAGE_LEAVE, LogCategory.FRAMEWORK))
- .isEqualTo(leaveCount)
+ assertThat(getEventCount(id, LogEvent.PAGE_ENTER, LogCategory.FRAMEWORK)).isEqualTo(entryCount)
+ assertThat(getEventCount(id, LogEvent.PAGE_LEAVE, LogCategory.FRAMEWORK)).isEqualTo(leaveCount)
}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt
index b139f28..0a1c05f 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt
@@ -22,6 +22,7 @@
import com.android.settingslib.spa.framework.util.genEntryId
import com.android.settingslib.spa.framework.util.genPageId
import com.android.settingslib.spa.tests.testutils.SpaEnvironmentForTest
+import com.android.settingslib.spa.tests.testutils.SppDialog
import com.android.settingslib.spa.tests.testutils.SppHome
import com.android.settingslib.spa.tests.testutils.SppLayer1
import com.android.settingslib.spa.tests.testutils.SppLayer2
@@ -39,26 +40,21 @@
@Test
fun testGetPageWithEntry() {
val pageWithEntry = entryRepository.getAllPageWithEntry()
- assertThat(pageWithEntry.size).isEqualTo(3)
- assertThat(
- entryRepository.getPageWithEntry(genPageId("SppHome"))
- ?.entries?.size
- ).isEqualTo(1)
- assertThat(
- entryRepository.getPageWithEntry(genPageId("SppLayer1"))
- ?.entries?.size
- ).isEqualTo(3)
- assertThat(
- entryRepository.getPageWithEntry(genPageId("SppLayer2"))
- ?.entries?.size
- ).isEqualTo(2)
+
+ assertThat(pageWithEntry).hasSize(4)
+ assertThat(entryRepository.getPageWithEntry(genPageId("SppHome"))?.entries)
+ .hasSize(2)
+ assertThat(entryRepository.getPageWithEntry(genPageId("SppLayer1"))?.entries)
+ .hasSize(3)
+ assertThat(entryRepository.getPageWithEntry(genPageId("SppLayer2"))?.entries)
+ .hasSize(2)
assertThat(entryRepository.getPageWithEntry(genPageId("SppWithParam"))).isNull()
}
@Test
fun testGetEntry() {
val entry = entryRepository.getAllEntries()
- assertThat(entry.size).isEqualTo(7)
+ assertThat(entry).hasSize(8)
assertThat(
entryRepository.getEntry(
genEntryId(
@@ -91,6 +87,16 @@
).isNotNull()
assertThat(
entryRepository.getEntry(
+ genEntryId(
+ "INJECT",
+ SppDialog.createSettingsPage(),
+ SppHome.createSettingsPage(),
+ SppDialog.createSettingsPage(),
+ )
+ )
+ ).isNotNull()
+ assertThat(
+ entryRepository.getEntry(
genEntryId("Layer1Entry1", SppLayer1.createSettingsPage())
)
).isNotNull()
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt
index 8576573..169c541 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt
@@ -25,39 +25,55 @@
@RunWith(AndroidJUnit4::class)
class SettingsPageProviderRepositoryTest {
@Test
- fun getStartPageTest() {
- val sppRepoEmpty = SettingsPageProviderRepository(emptyList(), emptyList())
+ fun rootPages_empty() {
+ val sppRepoEmpty = SettingsPageProviderRepository(emptyList())
+
assertThat(sppRepoEmpty.getDefaultStartPage()).isEqualTo("")
assertThat(sppRepoEmpty.getAllRootPages()).isEmpty()
-
- val nullPage = NullPageProvider.createSettingsPage()
- val sppRepoNull =
- SettingsPageProviderRepository(emptyList(), listOf(nullPage))
- assertThat(sppRepoNull.getDefaultStartPage()).isEqualTo("NULL")
- assertThat(sppRepoNull.getAllRootPages()).contains(nullPage)
-
- val rootPage1 = createSettingsPage(sppName = "Spp1", displayName = "Spp1")
- val rootPage2 = createSettingsPage(sppName = "Spp2", displayName = "Spp2")
- val sppRepo = SettingsPageProviderRepository(emptyList(), listOf(rootPage1, rootPage2))
- val allRoots = sppRepo.getAllRootPages()
- assertThat(sppRepo.getDefaultStartPage()).isEqualTo("Spp1")
- assertThat(allRoots.size).isEqualTo(2)
- assertThat(allRoots).contains(rootPage1)
- assertThat(allRoots).contains(rootPage2)
}
@Test
- fun getProviderTest() {
- val sppRepoEmpty = SettingsPageProviderRepository(emptyList(), emptyList())
+ fun rootPages_single() {
+ val nullPage = NullPageProvider.createSettingsPage()
+
+ val sppRepoNull = SettingsPageProviderRepository(
+ allPageProviders = emptyList(),
+ rootPages = listOf(nullPage),
+ )
+
+ assertThat(sppRepoNull.getDefaultStartPage()).isEqualTo("NULL")
+ assertThat(sppRepoNull.getAllRootPages()).containsExactly(nullPage)
+ }
+
+ @Test
+ fun rootPages_twoPages() {
+ val rootPage1 = createSettingsPage(sppName = "Spp1", displayName = "Spp1")
+ val rootPage2 = createSettingsPage(sppName = "Spp2", displayName = "Spp2")
+
+ val sppRepo = SettingsPageProviderRepository(
+ allPageProviders = emptyList(),
+ rootPages = listOf(rootPage1, rootPage2),
+ )
+
+ assertThat(sppRepo.getDefaultStartPage()).isEqualTo("Spp1")
+ assertThat(sppRepo.getAllRootPages()).containsExactly(rootPage1, rootPage2)
+ }
+
+ @Test
+ fun getProviderOrNull_empty() {
+ val sppRepoEmpty = SettingsPageProviderRepository(emptyList())
assertThat(sppRepoEmpty.getAllProviders()).isEmpty()
assertThat(sppRepoEmpty.getProviderOrNull("Spp")).isNull()
+ }
+ @Test
+ fun getProviderOrNull_single() {
val sppRepo = SettingsPageProviderRepository(listOf(
object : SettingsPageProvider {
override val name = "Spp"
}
- ), emptyList())
- assertThat(sppRepo.getAllProviders().size).isEqualTo(1)
+ ))
+ assertThat(sppRepo.getAllProviders()).hasSize(1)
assertThat(sppRepo.getProviderOrNull("Spp")).isNotNull()
assertThat(sppRepo.getProviderOrNull("SppUnknown")).isNull()
}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt
index 2755b4e..22a5ca3 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt
@@ -21,6 +21,8 @@
import android.content.Context
import android.content.Intent
import android.os.Bundle
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
import androidx.navigation.NavType
import androidx.navigation.navArgument
import com.android.settingslib.spa.framework.BrowseActivity
@@ -88,6 +90,7 @@
val owner = this.createSettingsPage()
return listOf(
SppLayer1.buildInject().setLink(fromPage = owner).build(),
+ SppDialog.buildInject().setLink(fromPage = owner).build(),
)
}
}
@@ -160,6 +163,21 @@
}
}
+object SppDialog : SettingsPageProvider {
+ override val name = "SppDialog"
+ override val navType = SettingsPageProvider.NavType.Dialog
+
+ const val CONTENT = "SppDialog Content"
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ Text(CONTENT)
+ }
+
+ fun buildInject() = SettingsEntryBuilder.createInject(this.createSettingsPage())
+ .setMacro { SimplePreferenceMacro(title = name, clickRoute = name) }
+}
+
object SppForSearch : SettingsPageProvider {
override val name = "SppForSearch"
@@ -223,6 +241,7 @@
navArgument("rt_param") { type = NavType.StringType },
)
},
+ SppDialog,
),
rootPages
)
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index 9560b8d..cdb8740 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -19,6 +19,7 @@
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHearingAid;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
@@ -42,18 +43,16 @@
BluetoothMediaDevice(
Context context,
CachedBluetoothDevice device,
- MediaRoute2Info info,
- String packageName) {
- this(context, device, info, packageName, null);
+ MediaRoute2Info info) {
+ this(context, device, info, null);
}
BluetoothMediaDevice(
Context context,
CachedBluetoothDevice device,
MediaRoute2Info info,
- String packageName,
RouteListingPreference.Item item) {
- super(context, info, packageName, item);
+ super(context, info, item);
mCachedDevice = device;
mAudioManager = context.getSystemService(AudioManager.class);
initDeviceRecord();
@@ -100,7 +99,12 @@
@Override
public String getId() {
- return MediaDeviceUtils.getId(mCachedDevice);
+ if (mCachedDevice.isHearingAidDevice()) {
+ if (mCachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
+ return Long.toString(mCachedDevice.getHiSyncId());
+ }
+ }
+ return mCachedDevice.getAddress();
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java
index 4e0ebd1..338fb87 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java
@@ -34,9 +34,8 @@
ComplexMediaDevice(
Context context,
MediaRoute2Info info,
- String packageName,
RouteListingPreference.Item item) {
- super(context, info, packageName, item);
+ super(context, info, item);
}
// MediaRoute2Info.getName was made public on API 34, but exists since API 30.
@@ -63,7 +62,7 @@
@Override
public String getId() {
- return MediaDeviceUtils.getId(mRouteInfo);
+ return mRouteInfo.getId();
}
public boolean isConnected() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index 012cbc0..1347dd1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -45,14 +45,13 @@
InfoMediaDevice(
Context context,
MediaRoute2Info info,
- String packageName,
RouteListingPreference.Item item) {
- super(context, info, packageName, item);
+ super(context, info, item);
initDeviceRecord();
}
- InfoMediaDevice(Context context, MediaRoute2Info info, String packageName) {
- this(context, info, packageName, null);
+ InfoMediaDevice(Context context, MediaRoute2Info info) {
+ this(context, info, null);
}
@Override
@@ -118,7 +117,7 @@
@Override
public String getId() {
- return MediaDeviceUtils.getId(mRouteInfo);
+ return mRouteInfo.getId();
}
public boolean isConnected() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index e5fce5b..581c7de 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -383,7 +383,7 @@
for (MediaRoute2Info route : getSelectableRoutes(info)) {
deviceList.add(
new InfoMediaDevice(
- mContext, route, mPackageName, mPreferenceItemMap.get(route.getId())));
+ mContext, route, mPreferenceItemMap.get(route.getId())));
}
return deviceList;
}
@@ -410,7 +410,7 @@
for (MediaRoute2Info route : getDeselectableRoutes(info)) {
deviceList.add(
new InfoMediaDevice(
- mContext, route, mPackageName, mPreferenceItemMap.get(route.getId())));
+ mContext, route, mPreferenceItemMap.get(route.getId())));
Log.d(TAG, route.getName() + " is deselectable for " + mPackageName);
}
return deviceList;
@@ -434,7 +434,7 @@
for (MediaRoute2Info route : getSelectedRoutes(info)) {
deviceList.add(
new InfoMediaDevice(
- mContext, route, mPackageName, mPreferenceItemMap.get(route.getId())));
+ mContext, route, mPreferenceItemMap.get(route.getId())));
}
return deviceList;
}
@@ -633,7 +633,6 @@
new InfoMediaDevice(
mContext,
route,
- mPackageName,
mPreferenceItemMap.get(route.getId()));
break;
case TYPE_BUILTIN_SPEAKER:
@@ -650,7 +649,6 @@
new PhoneMediaDevice(
mContext,
route,
- mPackageName,
mPreferenceItemMap.getOrDefault(route.getId(), null));
break;
case TYPE_HEARING_AID:
@@ -666,7 +664,6 @@
mContext,
cachedDevice,
route,
- mPackageName,
mPreferenceItemMap.getOrDefault(route.getId(), null));
}
break;
@@ -675,7 +672,6 @@
new ComplexMediaDevice(
mContext,
route,
- mPackageName,
mPreferenceItemMap.get(route.getId()));
break;
default:
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index dbc3bf7..ebcca42 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -567,7 +567,7 @@
final CachedBluetoothDevice cachedDevice =
cachedDeviceManager.findDevice(device);
if (isBondedMediaDevice(cachedDevice) && isMutingExpectedDevice(cachedDevice)) {
- return new BluetoothMediaDevice(mContext, cachedDevice, null, mPackageName);
+ return new BluetoothMediaDevice(mContext, cachedDevice, null);
}
}
return null;
@@ -614,7 +614,7 @@
mDisconnectedMediaDevices.clear();
for (CachedBluetoothDevice cachedDevice : cachedBluetoothDeviceList) {
final MediaDevice mediaDevice =
- new BluetoothMediaDevice(mContext, cachedDevice, null, mPackageName);
+ new BluetoothMediaDevice(mContext, cachedDevice, null);
if (!mMediaDevices.contains(mediaDevice)) {
cachedDevice.registerCallback(mDeviceAttributeChangeCallback);
mDisconnectedMediaDevices.add(mediaDevice);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index c8e4c0c..f2d9d14 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -121,16 +121,13 @@
protected final Context mContext;
protected final MediaRoute2Info mRouteInfo;
protected final RouteListingPreference.Item mItem;
- protected final String mPackageName;
MediaDevice(
Context context,
MediaRoute2Info info,
- String packageName,
RouteListingPreference.Item item) {
mContext = context;
mRouteInfo = info;
- mPackageName = packageName;
mItem = item;
setType(info);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
deleted file mode 100644
index b3a52b9..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 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.settingslib.media;
-
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHearingAid;
-import android.media.MediaRoute2Info;
-
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-
-/**
- * MediaDeviceUtils provides utility function for MediaDevice
- */
-public class MediaDeviceUtils {
- /**
- * Use CachedBluetoothDevice address to represent unique id
- *
- * @param cachedDevice the CachedBluetoothDevice
- * @return CachedBluetoothDevice address
- */
- public static String getId(CachedBluetoothDevice cachedDevice) {
- if (cachedDevice.isHearingAidDevice()) {
- if (cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
- return Long.toString(cachedDevice.getHiSyncId());
- }
- }
- return cachedDevice.getAddress();
- }
-
- /**
- * Use BluetoothDevice address to represent unique id
- *
- * @param bluetoothDevice the BluetoothDevice
- * @return BluetoothDevice address
- */
- public static String getId(BluetoothDevice bluetoothDevice) {
- return bluetoothDevice.getAddress();
- }
-
- /**
- * Use MediaRoute2Info id to represent unique id
- *
- * @param route the MediaRoute2Info
- * @return MediaRoute2Info id
- */
- public static String getId(MediaRoute2Info route) {
- return route.getId();
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 0676ce5..d6f1eab 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -117,16 +117,15 @@
return name.toString();
}
- PhoneMediaDevice(Context context, MediaRoute2Info info, String packageName) {
- this(context, info, packageName, null);
+ PhoneMediaDevice(Context context, MediaRoute2Info info) {
+ this(context, info, null);
}
PhoneMediaDevice(
Context context,
MediaRoute2Info info,
- String packageName,
RouteListingPreference.Item item) {
- super(context, info, packageName, item);
+ super(context, info, item);
mDeviceIconUtil = new DeviceIconUtil(mContext);
initDeviceRecord();
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
index f50802a..7061742 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
@@ -39,6 +39,8 @@
@RunWith(RobolectricTestRunner.class)
public class BluetoothMediaDeviceTest {
+ private static final String TEST_ADDRESS = "11:22:33:44:55:66";
+
@Mock
private CachedBluetoothDevice mDevice;
@@ -54,7 +56,7 @@
when(mDevice.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(true);
when(mDevice.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(true);
- mBluetoothMediaDevice = new BluetoothMediaDevice(mContext, mDevice, null, null, null);
+ mBluetoothMediaDevice = new BluetoothMediaDevice(mContext, mDevice, null, null);
}
@Test
@@ -111,4 +113,10 @@
assertThat(mBluetoothMediaDevice.getIcon() instanceof BitmapDrawable).isFalse();
}
+
+ @Test
+ public void getId_returnsCachedBluetoothDeviceAddress() {
+ when(mDevice.getAddress()).thenReturn(TEST_ADDRESS);
+ assertThat(mBluetoothMediaDevice.getId()).isEqualTo(TEST_ADDRESS);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
index a072c17..0665308 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
@@ -65,7 +65,7 @@
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
- mInfoMediaDevice = new InfoMediaDevice(mContext, mRouteInfo, TEST_PACKAGE_NAME);
+ mInfoMediaDevice = new InfoMediaDevice(mContext, mRouteInfo);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 2252b69..f0330c4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -547,7 +547,7 @@
@Test
public void connectDeviceWithoutPackageName_noSession_returnFalse() {
final MediaRoute2Info info = mock(MediaRoute2Info.class);
- final MediaDevice device = new InfoMediaDevice(mContext, info, TEST_PACKAGE_NAME);
+ final MediaDevice device = new InfoMediaDevice(mContext, info);
final List<RoutingSessionInfo> infos = new ArrayList<>();
@@ -623,7 +623,7 @@
routingSessionInfos.add(info);
final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
- final MediaDevice device = new InfoMediaDevice(mContext, route2Info, TEST_PACKAGE_NAME);
+ final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
final List<String> list = new ArrayList<>();
list.add(TEST_ID);
@@ -644,7 +644,7 @@
routingSessionInfos.add(info);
final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
- final MediaDevice device = new InfoMediaDevice(mContext, route2Info, TEST_PACKAGE_NAME);
+ final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
final List<String> list = new ArrayList<>();
list.add("fake_id");
@@ -674,7 +674,7 @@
routingSessionInfos.add(info);
final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
- final MediaDevice device = new InfoMediaDevice(mContext, route2Info, TEST_PACKAGE_NAME);
+ final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
final List<String> list = new ArrayList<>();
list.add(TEST_ID);
@@ -695,7 +695,7 @@
routingSessionInfos.add(info);
final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
- final MediaDevice device = new InfoMediaDevice(mContext, route2Info, TEST_PACKAGE_NAME);
+ final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
final List<String> list = new ArrayList<>();
list.add("fake_id");
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 926b41a..999e8d5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -116,8 +116,8 @@
when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
- mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mRouteInfo1, TEST_PACKAGE_NAME));
- mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2, TEST_PACKAGE_NAME);
+ mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mRouteInfo1));
+ mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2);
mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
mInfoMediaManager, "com.test.packagename");
mLocalMediaManager.mAudioManager = mAudioManager;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index 18055d9..098ab16 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -171,17 +171,17 @@
mBluetoothMediaDevice1 =
new BluetoothMediaDevice(
- mContext, mCachedDevice1, mBluetoothRouteInfo1, TEST_PACKAGE_NAME);
+ mContext, mCachedDevice1, mBluetoothRouteInfo1);
mBluetoothMediaDevice2 =
new BluetoothMediaDevice(
- mContext, mCachedDevice2, mBluetoothRouteInfo2, TEST_PACKAGE_NAME);
+ mContext, mCachedDevice2, mBluetoothRouteInfo2);
mBluetoothMediaDevice3 =
new BluetoothMediaDevice(
- mContext, mCachedDevice3, mBluetoothRouteInfo3, TEST_PACKAGE_NAME);
- mInfoMediaDevice1 = new InfoMediaDevice(mContext, mRouteInfo1, TEST_PACKAGE_NAME);
- mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2, TEST_PACKAGE_NAME);
- mInfoMediaDevice3 = new InfoMediaDevice(mContext, mRouteInfo3, TEST_PACKAGE_NAME);
- mPhoneMediaDevice = new PhoneMediaDevice(mContext, mPhoneRouteInfo, TEST_PACKAGE_NAME);
+ mContext, mCachedDevice3, mBluetoothRouteInfo3);
+ mInfoMediaDevice1 = new InfoMediaDevice(mContext, mRouteInfo1);
+ mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2);
+ mInfoMediaDevice3 = new InfoMediaDevice(mContext, mRouteInfo3);
+ mPhoneMediaDevice = new PhoneMediaDevice(mContext, mPhoneRouteInfo);
}
@Test
@@ -316,7 +316,7 @@
when(phoneRouteInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES);
final PhoneMediaDevice phoneMediaDevice =
- new PhoneMediaDevice(mContext, phoneRouteInfo, TEST_PACKAGE_NAME);
+ new PhoneMediaDevice(mContext, phoneRouteInfo);
mMediaDevices.add(mBluetoothMediaDevice1);
mMediaDevices.add(phoneMediaDevice);
@@ -332,7 +332,7 @@
when(phoneRouteInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES);
final PhoneMediaDevice phoneMediaDevice =
- new PhoneMediaDevice(mContext, phoneRouteInfo, TEST_PACKAGE_NAME);
+ new PhoneMediaDevice(mContext, phoneRouteInfo);
mMediaDevices.add(mInfoMediaDevice1);
mMediaDevices.add(phoneMediaDevice);
@@ -483,7 +483,7 @@
public void getFeatures_noRouteInfo_returnEmptyList() {
mBluetoothMediaDevice1 =
new BluetoothMediaDevice(
- mContext, mCachedDevice1, null /* MediaRoute2Info */, TEST_PACKAGE_NAME);
+ mContext, mCachedDevice1, /* MediaRoute2Info */ null);
assertThat(mBluetoothMediaDevice1.getFeatures().size()).isEqualTo(0);
}
@@ -498,10 +498,9 @@
mContext,
mCachedDevice1,
null /* MediaRoute2Info */,
- TEST_PACKAGE_NAME,
mItem);
mPhoneMediaDevice =
- new PhoneMediaDevice(mContext, mPhoneRouteInfo, TEST_PACKAGE_NAME, mItem);
+ new PhoneMediaDevice(mContext, mPhoneRouteInfo, mItem);
assertThat(mBluetoothMediaDevice1.getSelectionBehavior()).isEqualTo(
SELECTION_BEHAVIOR_TRANSFER);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java
deleted file mode 100644
index 30a6ad2..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.media;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.bluetooth.BluetoothDevice;
-import android.media.MediaRoute2Info;
-
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-
-@RunWith(RobolectricTestRunner.class)
-public class MediaDeviceUtilsTest {
-
- private static final String TEST_ADDRESS = "11:22:33:44:55:66";
- private static final String TEST_ROUTE_ID = "test_route_id";
-
- @Mock
- private CachedBluetoothDevice mCachedDevice;
- @Mock
- private BluetoothDevice mBluetoothDevice;
- @Mock
- private MediaRoute2Info mRouteInfo;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void getId_returnCachedBluetoothDeviceAddress() {
- when(mCachedDevice.getAddress()).thenReturn(TEST_ADDRESS);
-
- final String id = MediaDeviceUtils.getId(mCachedDevice);
-
- assertThat(id).isEqualTo(TEST_ADDRESS);
- }
-
- @Test
- public void getId_returnBluetoothDeviceAddress() {
- when(mBluetoothDevice.getAddress()).thenReturn(TEST_ADDRESS);
-
- final String id = MediaDeviceUtils.getId(mBluetoothDevice);
-
- assertThat(id).isEqualTo(TEST_ADDRESS);
- }
-
- @Test
- public void getId_returnRouteInfoId() {
- when(mRouteInfo.getId()).thenReturn(TEST_ROUTE_ID);
-
- final String id = MediaDeviceUtils.getId(mRouteInfo);
-
- assertThat(id).isEqualTo(TEST_ROUTE_ID);
- }
-}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index d6e8d26..2e39adc 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -69,6 +69,7 @@
Settings.Global.PRIVATE_DNS_SPECIFIER,
Settings.Global.SOFT_AP_TIMEOUT_ENABLED,
Settings.Global.ZEN_DURATION,
+ Settings.Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE_USER_PREFERENCE,
Settings.Global.REVERSE_CHARGING_AUTO_ON,
Settings.Global.CHARGING_VIBRATION_ENABLED,
Settings.Global.AWARE_ALLOWED,
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 59c3cd3..e7d7bb0 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -19,93 +19,105 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.provider.Settings;
+import com.android.server.display.feature.flags.Flags;
+
+import java.util.ArrayList;
+import java.util.List;
+
/** Information about the system settings to back up */
public class SystemSettings {
/**
- * Settings to backup.
+ * Settings to back up.
*
* NOTE: Settings are backed up and restored in the order they appear
* in this array. If you have one setting depending on another,
* make sure that they are ordered appropriately.
*/
@UnsupportedAppUsage
- public static final String[] SETTINGS_TO_BACKUP = {
- Settings.System.STAY_ON_WHILE_PLUGGED_IN, // moved to global
- Settings.System.WIFI_USE_STATIC_IP,
- Settings.System.WIFI_STATIC_IP,
- Settings.System.WIFI_STATIC_GATEWAY,
- Settings.System.WIFI_STATIC_NETMASK,
- Settings.System.WIFI_STATIC_DNS1,
- Settings.System.WIFI_STATIC_DNS2,
- Settings.System.BLUETOOTH_DISCOVERABILITY,
- Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT,
- Settings.System.FONT_SCALE,
- Settings.System.DIM_SCREEN,
- Settings.System.SCREEN_OFF_TIMEOUT,
- Settings.System.SCREEN_BRIGHTNESS_MODE,
- Settings.System.ADAPTIVE_SLEEP, // moved to secure
- Settings.System.APPLY_RAMPING_RINGER,
- Settings.System.VIBRATE_INPUT_DEVICES,
- Settings.System.MODE_RINGER_STREAMS_AFFECTED,
- Settings.System.TEXT_AUTO_REPLACE,
- Settings.System.TEXT_AUTO_CAPS,
- Settings.System.TEXT_AUTO_PUNCTUATE,
- Settings.System.TEXT_SHOW_PASSWORD,
- Settings.System.AUTO_TIME, // moved to global
- Settings.System.AUTO_TIME_ZONE, // moved to global
- Settings.System.TIME_12_24,
- Settings.System.DTMF_TONE_WHEN_DIALING,
- Settings.System.DTMF_TONE_TYPE_WHEN_DIALING,
- Settings.System.HEARING_AID,
- Settings.System.TTY_MODE,
- Settings.System.MASTER_MONO,
- Settings.System.MASTER_BALANCE,
- Settings.System.FOLD_LOCK_BEHAVIOR,
- Settings.System.SOUND_EFFECTS_ENABLED,
- Settings.System.HAPTIC_FEEDBACK_ENABLED,
- Settings.System.POWER_SOUNDS_ENABLED, // moved to global
- Settings.System.DOCK_SOUNDS_ENABLED, // moved to global
- Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
- Settings.System.SHOW_WEB_SUGGESTIONS,
- Settings.System.SIP_CALL_OPTIONS,
- Settings.System.SIP_RECEIVE_CALLS,
- Settings.System.POINTER_SPEED,
- Settings.System.VIBRATE_ON,
- Settings.System.VIBRATE_WHEN_RINGING,
- Settings.System.RINGTONE,
- Settings.System.LOCK_TO_APP_ENABLED,
- Settings.System.NOTIFICATION_SOUND,
- Settings.System.ACCELEROMETER_ROTATION,
- Settings.System.SHOW_BATTERY_PERCENT,
- Settings.System.ALARM_VIBRATION_INTENSITY,
- Settings.System.MEDIA_VIBRATION_INTENSITY,
- Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
- Settings.System.RING_VIBRATION_INTENSITY,
- Settings.System.HAPTIC_FEEDBACK_INTENSITY,
- Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY,
- Settings.System.KEYBOARD_VIBRATION_ENABLED,
- Settings.System.HAPTIC_FEEDBACK_ENABLED,
- Settings.System.DISPLAY_COLOR_MODE_VENDOR_HINT, // must precede DISPLAY_COLOR_MODE
- Settings.System.DISPLAY_COLOR_MODE,
- Settings.System.ALARM_ALERT,
- Settings.System.NOTIFICATION_LIGHT_PULSE,
- Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED,
- Settings.System.CLOCKWORK_BLUETOOTH_SETTINGS_PREF,
- Settings.System.UNREAD_NOTIFICATION_DOT_INDICATOR,
- Settings.System.AUTO_LAUNCH_MEDIA_CONTROLS,
- Settings.System.LOCALE_PREFERENCES,
- Settings.System.TOUCHPAD_POINTER_SPEED,
- Settings.System.TOUCHPAD_NATURAL_SCROLLING,
- Settings.System.TOUCHPAD_TAP_TO_CLICK,
- Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE,
- Settings.System.CAMERA_FLASH_NOTIFICATION,
- Settings.System.SCREEN_FLASH_NOTIFICATION,
- Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
- Settings.System.PEAK_REFRESH_RATE,
- Settings.System.MIN_REFRESH_RATE,
- Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
- Settings.System.NOTIFICATION_COOLDOWN_ALL,
- Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
- };
+ public static final String[] SETTINGS_TO_BACKUP = getSettingsToBackUp();
+
+ private static String[] getSettingsToBackUp() {
+ List<String> settings = new ArrayList<>(List.of(
+ Settings.System.STAY_ON_WHILE_PLUGGED_IN, // moved to global
+ Settings.System.WIFI_USE_STATIC_IP,
+ Settings.System.WIFI_STATIC_IP,
+ Settings.System.WIFI_STATIC_GATEWAY,
+ Settings.System.WIFI_STATIC_NETMASK,
+ Settings.System.WIFI_STATIC_DNS1,
+ Settings.System.WIFI_STATIC_DNS2,
+ Settings.System.BLUETOOTH_DISCOVERABILITY,
+ Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT,
+ Settings.System.FONT_SCALE,
+ Settings.System.DIM_SCREEN,
+ Settings.System.SCREEN_OFF_TIMEOUT,
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.ADAPTIVE_SLEEP, // moved to secure
+ Settings.System.APPLY_RAMPING_RINGER,
+ Settings.System.VIBRATE_INPUT_DEVICES,
+ Settings.System.MODE_RINGER_STREAMS_AFFECTED,
+ Settings.System.TEXT_AUTO_REPLACE,
+ Settings.System.TEXT_AUTO_CAPS,
+ Settings.System.TEXT_AUTO_PUNCTUATE,
+ Settings.System.TEXT_SHOW_PASSWORD,
+ Settings.System.AUTO_TIME, // moved to global
+ Settings.System.AUTO_TIME_ZONE, // moved to global
+ Settings.System.TIME_12_24,
+ Settings.System.DTMF_TONE_WHEN_DIALING,
+ Settings.System.DTMF_TONE_TYPE_WHEN_DIALING,
+ Settings.System.HEARING_AID,
+ Settings.System.TTY_MODE,
+ Settings.System.MASTER_MONO,
+ Settings.System.MASTER_BALANCE,
+ Settings.System.FOLD_LOCK_BEHAVIOR,
+ Settings.System.SOUND_EFFECTS_ENABLED,
+ Settings.System.HAPTIC_FEEDBACK_ENABLED,
+ Settings.System.POWER_SOUNDS_ENABLED, // moved to global
+ Settings.System.DOCK_SOUNDS_ENABLED, // moved to global
+ Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
+ Settings.System.SHOW_WEB_SUGGESTIONS,
+ Settings.System.SIP_CALL_OPTIONS,
+ Settings.System.SIP_RECEIVE_CALLS,
+ Settings.System.POINTER_SPEED,
+ Settings.System.VIBRATE_ON,
+ Settings.System.VIBRATE_WHEN_RINGING,
+ Settings.System.RINGTONE,
+ Settings.System.LOCK_TO_APP_ENABLED,
+ Settings.System.NOTIFICATION_SOUND,
+ Settings.System.ACCELEROMETER_ROTATION,
+ Settings.System.SHOW_BATTERY_PERCENT,
+ Settings.System.ALARM_VIBRATION_INTENSITY,
+ Settings.System.MEDIA_VIBRATION_INTENSITY,
+ Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+ Settings.System.RING_VIBRATION_INTENSITY,
+ Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+ Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY,
+ Settings.System.KEYBOARD_VIBRATION_ENABLED,
+ Settings.System.HAPTIC_FEEDBACK_ENABLED,
+ Settings.System.DISPLAY_COLOR_MODE_VENDOR_HINT, // must precede DISPLAY_COLOR_MODE
+ Settings.System.DISPLAY_COLOR_MODE,
+ Settings.System.ALARM_ALERT,
+ Settings.System.NOTIFICATION_LIGHT_PULSE,
+ Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED,
+ Settings.System.CLOCKWORK_BLUETOOTH_SETTINGS_PREF,
+ Settings.System.UNREAD_NOTIFICATION_DOT_INDICATOR,
+ Settings.System.AUTO_LAUNCH_MEDIA_CONTROLS,
+ Settings.System.LOCALE_PREFERENCES,
+ Settings.System.TOUCHPAD_POINTER_SPEED,
+ Settings.System.TOUCHPAD_NATURAL_SCROLLING,
+ Settings.System.TOUCHPAD_TAP_TO_CLICK,
+ Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE,
+ Settings.System.CAMERA_FLASH_NOTIFICATION,
+ Settings.System.SCREEN_FLASH_NOTIFICATION,
+ Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
+ Settings.System.NOTIFICATION_COOLDOWN_ALL,
+ Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED
+ ));
+ if (Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) {
+ settings.add(Settings.System.PEAK_REFRESH_RATE);
+ settings.add(Settings.System.MIN_REFRESH_RATE);
+ }
+ return settings.toArray(new String[0]);
+ }
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index f8bdcf6..5022395 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -210,6 +210,8 @@
VALIDATORS.put(Global.POWER_BUTTON_LONG_PRESS_DURATION_MS, NONE_NEGATIVE_LONG_VALIDATOR);
VALIDATORS.put(Global.STYLUS_EVER_USED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(
+ Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE_USER_PREFERENCE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.HAS_PAY_TOKENS, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.GMS_CHECKIN_TIMEOUT_MIN, ANY_INTEGER_VALIDATOR);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 6ad10cc..1481d97 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -33,6 +33,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.display.feature.flags.Flags;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,59 +55,6 @@
public static final String HYBRID_SYSUI_BATTERY_WARNING_FLAGS =
"hybrid_sysui_battery_warning_flags";
- /**
- * The following denylists contain settings that should *not* be backed up and restored to
- * another device. As a general rule, anything that is not user configurable should be
- * denied (and conversely, things that *are* user configurable *should* be backed up)
- */
- private static final Set<String> BACKUP_DENY_LIST_SYSTEM_SETTINGS =
- newHashSet(
- Settings.System.ADVANCED_SETTINGS, // candidate for backup?
- Settings.System.ALARM_ALERT_CACHE, // internal cache
- Settings.System.APPEND_FOR_LAST_AUDIBLE, // suffix deprecated since API 2
- Settings.System.EGG_MODE, // I am the lolrus
- Settings.System.END_BUTTON_BEHAVIOR, // bug?
- Settings.System
- .HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, // candidate for backup?
- Settings.System.LOCKSCREEN_DISABLED, // ?
- Settings.System.MEDIA_BUTTON_RECEIVER, // candidate for backup?
- Settings.System.MUTE_STREAMS_AFFECTED, // candidate for backup?
- Settings.System.NOTIFICATION_SOUND_CACHE, // internal cache
- Settings.System.POINTER_LOCATION, // backup candidate?
- Settings.System.DEBUG_ENABLE_ENHANCED_CALL_BLOCKING, // used for testing only
- Settings.System.RINGTONE_CACHE, // internal cache
- Settings.System.SCREEN_BRIGHTNESS, // removed in P
- Settings.System.SETUP_WIZARD_HAS_RUN, // Only used by SuW
- Settings.System.SHOW_GTALK_SERVICE_STATUS, // candidate for backup?
- Settings.System.SHOW_TOUCHES,
- Settings.System.SHOW_KEY_PRESSES,
- Settings.System.SHOW_ROTARY_INPUT,
- Settings.System.SIP_ADDRESS_ONLY, // value, not a setting
- Settings.System.SIP_ALWAYS, // value, not a setting
- Settings.System.SYSTEM_LOCALES, // bug?
- Settings.System.USER_ROTATION, // backup candidate?
- Settings.System.VIBRATE_IN_SILENT, // deprecated?
- Settings.System.VOLUME_ACCESSIBILITY, // used internally, changing value will
- // not change volume
- Settings.System.VOLUME_ALARM, // deprecated since API 2?
- Settings.System.VOLUME_ASSISTANT, // candidate for backup?
- Settings.System.VOLUME_BLUETOOTH_SCO, // deprecated since API 2?
- Settings.System.VOLUME_MASTER, // candidate for backup?
- Settings.System.VOLUME_MUSIC, // deprecated since API 2?
- Settings.System.VOLUME_NOTIFICATION, // deprecated since API 2?
- Settings.System.VOLUME_RING, // deprecated since API 2?
- Settings.System.VOLUME_SYSTEM, // deprecated since API 2?
- Settings.System.VOLUME_VOICE, // deprecated since API 2?
- Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
- Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
- Settings.System.SCREEN_BRIGHTNESS_FLOAT,
- Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
- Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
- Settings.System.WEAR_TTS_PREWARM_ENABLED,
- Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
- Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
- );
-
private static final Set<String> BACKUP_DENY_LIST_GLOBAL_SETTINGS =
newHashSet(
Settings.Global.ACTIVITY_MANAGER_CONSTANTS,
@@ -737,6 +686,7 @@
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS,
Settings.Secure.CONTENT_CAPTURE_ENABLED,
Settings.Secure.DEFAULT_INPUT_METHOD,
+ Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD,
Settings.Secure.DEVICE_PAIRED,
Settings.Secure.DIALER_DEFAULT_APPLICATION,
Settings.Secure.DISABLED_PRINT_SERVICES,
@@ -862,7 +812,7 @@
checkSettingsBackedUpOrDenied(
getCandidateSettings(Settings.System.class),
newHashSet(SystemSettings.SETTINGS_TO_BACKUP),
- BACKUP_DENY_LIST_SYSTEM_SETTINGS);
+ getBackUpDenyListSystemSettings());
}
@Test
@@ -937,6 +887,69 @@
checkSettingsBackedUpOrDenied(allSettings, keys, BACKUP_DENY_LIST_SECURE_SETTINGS);
}
+ /**
+ * The following denylists contain settings that should *not* be backed up and restored to
+ * another device. As a general rule, anything that is not user configurable should be
+ * denied (and conversely, things that *are* user configurable *should* be backed up)
+ */
+ private static Set<String> getBackUpDenyListSystemSettings() {
+ Set<String> settings =
+ newHashSet(
+ Settings.System.ADVANCED_SETTINGS, // candidate for backup?
+ Settings.System.ALARM_ALERT_CACHE, // internal cache
+ Settings.System.APPEND_FOR_LAST_AUDIBLE, // suffix deprecated since API 2
+ Settings.System.EGG_MODE, // I am the lolrus
+ Settings.System.END_BUTTON_BEHAVIOR, // bug?
+ Settings.System
+ .HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY,
+ // candidate for backup?
+ Settings.System.LOCKSCREEN_DISABLED, // ?
+ Settings.System.MEDIA_BUTTON_RECEIVER, // candidate for backup?
+ Settings.System.MUTE_STREAMS_AFFECTED, // candidate for backup?
+ Settings.System.NOTIFICATION_SOUND_CACHE, // internal cache
+ Settings.System.POINTER_LOCATION, // backup candidate?
+ Settings.System.DEBUG_ENABLE_ENHANCED_CALL_BLOCKING,
+ // used for testing only
+ Settings.System.RINGTONE_CACHE, // internal cache
+ Settings.System.SCREEN_BRIGHTNESS, // removed in P
+ Settings.System.SETUP_WIZARD_HAS_RUN, // Only used by SuW
+ Settings.System.SHOW_GTALK_SERVICE_STATUS, // candidate for backup?
+ Settings.System.SHOW_TOUCHES,
+ Settings.System.SHOW_KEY_PRESSES,
+ Settings.System.SHOW_ROTARY_INPUT,
+ Settings.System.SIP_ADDRESS_ONLY, // value, not a setting
+ Settings.System.SIP_ALWAYS, // value, not a setting
+ Settings.System.SYSTEM_LOCALES, // bug?
+ Settings.System.USER_ROTATION, // backup candidate?
+ Settings.System.VIBRATE_IN_SILENT, // deprecated?
+ Settings.System.VOLUME_ACCESSIBILITY,
+ // used internally, changing value will
+ // not change volume
+ Settings.System.VOLUME_ALARM, // deprecated since API 2?
+ Settings.System.VOLUME_ASSISTANT, // candidate for backup?
+ Settings.System.VOLUME_BLUETOOTH_SCO, // deprecated since API 2?
+ Settings.System.VOLUME_MASTER, // candidate for backup?
+ Settings.System.VOLUME_MUSIC, // deprecated since API 2?
+ Settings.System.VOLUME_NOTIFICATION, // deprecated since API 2?
+ Settings.System.VOLUME_RING, // deprecated since API 2?
+ Settings.System.VOLUME_SYSTEM, // deprecated since API 2?
+ Settings.System.VOLUME_VOICE, // deprecated since API 2?
+ Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
+ Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
+ Settings.System.SCREEN_BRIGHTNESS_FLOAT,
+ Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
+ Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
+ Settings.System.WEAR_TTS_PREWARM_ENABLED,
+ Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
+ Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
+ );
+ if (!Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) {
+ settings.add(Settings.System.MIN_REFRESH_RATE);
+ settings.add(Settings.System.PEAK_REFRESH_RATE);
+ }
+ return settings;
+ }
+
private static void checkSettingsBackedUpOrDenied(
Set<String> settings, Set<String> settingsToBackup, Set<String> denylist) {
Set<String> settingsNotBackedUp = difference(settings, settingsToBackup);
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index cc5dfc6..42107b7 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -65,6 +65,7 @@
"androidx.compose.runtime_runtime",
"androidx.compose.material3_material3",
+ "androidx.compose.material_material-icons-extended",
"androidx.activity_activity-compose",
"androidx.compose.animation_animation-graphics",
],
diff --git a/packages/SystemUI/aconfig/predictive_back.aconfig b/packages/SystemUI/aconfig/predictive_back.aconfig
new file mode 100644
index 0000000..1ad1666
--- /dev/null
+++ b/packages/SystemUI/aconfig/predictive_back.aconfig
@@ -0,0 +1,22 @@
+package: "com.android.systemui"
+
+flag {
+ name: "predictive_back_sysui"
+ namespace: "systemui"
+ description: "Predictive Back Dispatching for SysUI"
+ bug: "309545085"
+}
+
+flag {
+ name: "predictive_back_animate_shade"
+ namespace: "systemui"
+ description: "Enable Shade Animations"
+ bug: "309545085"
+}
+
+flag {
+ name: "predictive_back_animate_bouncer"
+ namespace: "systemui"
+ description: "Enable Predictive Back Animation in Bouncer"
+ bug: "309545085"
+}
\ No newline at end of file
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index aa0903c..5b21854 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -165,7 +165,7 @@
name: "qs_new_tiles"
namespace: "systemui"
description: "Use the new tiles in the Quick Settings. Should have no behavior changes."
- bug: "241772429"
+ bug: "311147395"
}
flag {
@@ -308,6 +308,13 @@
}
flag {
+ name: "run_fingerprint_detect_on_dismissible_keyguard"
+ namespace: "systemui"
+ description: "Run fingerprint detect instead of authenticate if the keyguard is dismissible."
+ bug: "311145851"
+}
+
+flag {
name: "bluetooth_qs_tile_dialog_auto_on_toggle"
namespace: "systemui"
description: "Displays the auto on toggle in the bluetooth QS tile dialog"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt
index 23fcb69..867bbb7d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt
@@ -65,12 +65,33 @@
return dest;
}
- // Return range [-1, 1].
+ // Integer mod. GLSL es 1.0 doesn't have integer mod :(
+ int imod(int a, int b) {
+ return a - (b * (a / b));
+ }
+
+ ivec3 imod(ivec3 a, int b) {
+ return ivec3(imod(a.x, b), imod(a.y, b), imod(a.z, b));
+ }
+
+ // Integer based hash function with the return range of [-1, 1].
vec3 hash(vec3 p) {
- p = fract(p * vec3(.3456, .1234, .9876));
- p += dot(p, p.yxz + 43.21);
- p = (p.xxy + p.yxx) * p.zyx;
- return (fract(sin(p) * 4567.1234567) - .5) * 2.;
+ ivec3 v = ivec3(p);
+ v = v * 1671731 + 10139267;
+
+ v.x += v.y * v.z;
+ v.y += v.z * v.x;
+ v.z += v.x * v.y;
+
+ ivec3 v2 = v / 65536; // v >> 16
+ v = imod((10 - imod((v + v2), 10)), 10); // v ^ v2
+
+ v.x += v.y * v.z;
+ v.y += v.z * v.x;
+ v.z += v.x * v.y;
+
+ // Use sin and cos to map the range to [-1, 1].
+ return vec3(sin(float(v.x)), cos(float(v.y)), sin(float(v.z)));
}
// Skew factors (non-uniform).
diff --git a/packages/SystemUI/compose/features/Android.bp b/packages/SystemUI/compose/features/Android.bp
index 796abf4b..5ab2235 100644
--- a/packages/SystemUI/compose/features/Android.bp
+++ b/packages/SystemUI/compose/features/Android.bp
@@ -38,6 +38,7 @@
"androidx.compose.runtime_runtime",
"androidx.compose.animation_animation-graphics",
"androidx.compose.material3_material3",
+ "androidx.compose.material_material-icons-extended",
"androidx.activity_activity-compose",
],
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 5a4e0a9..8bd5ddb 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -205,8 +205,12 @@
list[index].size.dp().value,
)
if (viewModel.isEditMode && dragDropState != null) {
- DraggableItem(dragDropState = dragDropState, enabled = true, index = index) {
- isDragging ->
+ DraggableItem(
+ dragDropState = dragDropState,
+ enabled = true,
+ index = index,
+ size = size
+ ) { isDragging ->
val elevation by animateDpAsState(if (isDragging) 4.dp else 1.dp)
CommunalContent(
modifier = cardModifier,
@@ -326,7 +330,7 @@
elevation: Dp = 0.dp,
) {
when (model) {
- is CommunalContentModel.Widget -> WidgetContent(model, size, elevation, modifier)
+ is CommunalContentModel.Widget -> WidgetContent(viewModel, model, size, elevation, modifier)
is CommunalContentModel.WidgetPlaceholder -> WidgetPlaceholderContent(size)
is CommunalContentModel.Smartspace -> SmartspaceContent(model, modifier)
is CommunalContentModel.Tutorial -> TutorialContent(modifier)
@@ -347,6 +351,7 @@
@Composable
private fun WidgetContent(
+ viewModel: BaseCommunalViewModel,
model: CommunalContentModel.Widget,
size: SizeF,
elevation: Dp,
@@ -359,9 +364,18 @@
AndroidView(
modifier = modifier,
factory = { context ->
- model.appWidgetHost
- .createView(context, model.appWidgetId, model.providerInfo)
- .apply { updateAppWidgetSize(Bundle.EMPTY, listOf(size)) }
+ // The AppWidgetHostView will inherit the interaction handler from the
+ // AppWidgetHost. So set the interaction handler here before creating the view, and
+ // then clear it after the view is created. This is a workaround due to the fact
+ // that the interaction handler cannot be specified when creating the view,
+ // and there are race conditions if it is set after the view is created.
+ model.appWidgetHost.setInteractionHandler(viewModel.getInteractionHandler())
+ val view =
+ model.appWidgetHost
+ .createView(context, model.appWidgetId, model.providerInfo)
+ .apply { updateAppWidgetSize(Bundle.EMPTY, listOf(size)) }
+ model.appWidgetHost.setInteractionHandler(null)
+ view
},
// For reusing composition in lazy lists.
onReset = {},
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
index 0d460aa8..1b40de4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
@@ -16,6 +16,7 @@
package com.android.systemui.communal.ui.compose
+import android.util.SizeF
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
import androidx.compose.foundation.gestures.scrollBy
@@ -233,6 +234,7 @@
dragDropState: GridDragDropState,
index: Int,
enabled: Boolean,
+ size: SizeF,
modifier: Modifier = Modifier,
content: @Composable (isDragging: Boolean) -> Unit
) {
@@ -250,7 +252,11 @@
} else {
Modifier.animateItemPlacement()
}
- Box(modifier = modifier.then(draggingModifier), propagateMinConstraints = true) {
- content(dragging)
+
+ Box(modifier) {
+ if (dragging) {
+ WidgetPlaceholderContent(size)
+ }
+ Box(modifier = draggingModifier, propagateMinConstraints = true) { content(dragging) }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 1fc2843..c8461d2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -45,10 +45,10 @@
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
import com.android.systemui.classifier.FalsingA11yDelegate
import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.log.SessionTracker
@@ -140,7 +140,7 @@
@Mock private lateinit var viewMediatorCallback: ViewMediatorCallback
@Mock private lateinit var audioManager: AudioManager
@Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
- @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+ @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
@Mock private lateinit var faceAuthAccessibilityDelegate: FaceAuthAccessibilityDelegate
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
@Mock private lateinit var postureController: DevicePostureController
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index e5da1f8..36aa441 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -85,10 +85,10 @@
import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
@@ -336,7 +336,7 @@
mSessionTracker,
mAlternateBouncerInteractor,
mInputManager,
- mock(KeyguardFaceAuthInteractor.class),
+ mock(DeviceEntryFaceAuthInteractor.class),
mUdfpsKeyguardAccessibilityDelegate,
mSelectedUserInteractor,
mFpsUnlockTracker,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index 1f8854f..335ac9d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -32,11 +32,11 @@
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -111,7 +111,7 @@
FakeTrustRepository(),
testScope.backgroundScope,
mSelectedUserInteractor,
- mock(KeyguardFaceAuthInteractor::class.java),
+ mock(DeviceEntryFaceAuthInteractor::class.java),
)
mAlternateBouncerInteractor =
AlternateBouncerInteractor(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 99c1874..93ba6a4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -25,7 +25,7 @@
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
import com.google.common.truth.Truth.assertThat
@@ -46,7 +46,7 @@
@RunWith(AndroidJUnit4::class)
class BouncerInteractorTest : SysuiTestCase() {
- @Mock private lateinit var keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor
+ @Mock private lateinit var mDeviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
@@ -67,7 +67,7 @@
underTest =
utils.bouncerInteractor(
authenticationInteractor = authenticationInteractor,
- keyguardFaceAuthInteractor = keyguardFaceAuthInteractor,
+ deviceEntryFaceAuthInteractor = mDeviceEntryFaceAuthInteractor,
)
}
@@ -306,7 +306,7 @@
fun intentionalUserInputEvent_notifiesFaceAuthInteractor() =
testScope.runTest {
underTest.onIntentionalUserInput()
- verify(keyguardFaceAuthInteractor).onPrimaryBouncerUserInput()
+ verify(mDeviceEntryFaceAuthInteractor).onPrimaryBouncerUserInput()
}
companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
index bdf5041..c8560c3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
@@ -26,9 +26,9 @@
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.TrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.utils.os.FakeHandler
@@ -54,7 +54,7 @@
@Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
- @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+ @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
private val mainHandler = FakeHandler(Looper.getMainLooper())
private lateinit var underTest: PrimaryBouncerInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
index a3bf3f4..a0c2acc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
@@ -30,9 +30,9 @@
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.TrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -63,7 +63,7 @@
@Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
@Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
- @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+ @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
lateinit var bouncerInteractor: PrimaryBouncerInteractor
private val mainHandler = FakeHandler(Looper.getMainLooper())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index 314dfdf..f2f9705 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -33,14 +33,15 @@
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.ui.MediaHost
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import javax.inject.Provider
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -56,7 +57,8 @@
@Mock private lateinit var shadeViewController: ShadeViewController
@Mock private lateinit var powerManager: PowerManager
- private lateinit var testScope: TestScope
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
private lateinit var keyguardRepository: FakeKeyguardRepository
private lateinit var communalRepository: FakeCommunalRepository
@@ -71,8 +73,6 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- testScope = TestScope()
-
val withDeps = CommunalInteractorFactory.create()
keyguardRepository = withDeps.keyguardRepository
communalRepository = withDeps.communalRepository
@@ -130,4 +130,17 @@
assertThat(communalContent?.get(1))
.isInstanceOf(CommunalContentModel.Widget::class.java)
}
+
+ @Test
+ fun interactionHandlerIgnoresClicks() {
+ val interactionHandler = underTest.getInteractionHandler()
+ assertThat(
+ interactionHandler.onInteraction(
+ /* view = */ mock(),
+ /* pendingIntent = */ mock(),
+ /* response = */ mock()
+ )
+ )
+ .isEqualTo(false)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index 8a71168..182cc5d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.communal.widgets.WidgetInteractionHandler
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.media.controls.ui.MediaHost
@@ -84,6 +85,7 @@
underTest =
CommunalViewModel(
withDeps.communalInteractor,
+ WidgetInteractionHandler(mock()),
withDeps.tutorialInteractor,
Provider { shadeViewController },
powerManager,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
similarity index 96%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 941d67f..6a14220 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.keyguard.data.repository
+package com.android.systemui.deviceentry.data.repository
import android.app.StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
import android.app.StatusBarManager.SESSION_KEYGUARD
@@ -37,10 +37,6 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId.fakeInstanceId
import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.FaceAuthUiEvent
-import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
-import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED
-import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
@@ -53,21 +49,32 @@
import com.android.systemui.coroutines.FlowValue
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
+import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
import com.android.systemui.display.data.repository.FakeDisplayRepository
import com.android.systemui.display.data.repository.display
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.KEYGUARD_WM_STATE_REFACTOR
+import com.android.systemui.keyguard.data.repository.BiometricType
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
-import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.log.FaceAuthenticationLogger
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index efd4f9b..530d127d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -265,7 +265,8 @@
powerInteractor = powerInteractor,
bouncerInteractor = bouncerInteractor,
simBouncerInteractor = dagger.Lazy { utils.simBouncerInteractor },
- authenticationInteractor = dagger.Lazy { utils.authenticationInteractor() }
+ authenticationInteractor = dagger.Lazy { utils.authenticationInteractor() },
+ windowController = mock(),
)
startable.start()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 2e4986d..dd22976 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -37,6 +37,7 @@
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -46,18 +47,24 @@
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Mock
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
@EnableFlags(AconfigFlags.FLAG_SCENE_CONTAINER)
class SceneContainerStartableTest : SysuiTestCase() {
+ @Mock private lateinit var windowController: NotificationShadeWindowController
+
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val sceneInteractor = utils.sceneInteractor()
@@ -77,22 +84,30 @@
private val falsingCollector: FalsingCollector = mock()
private val powerInteractor = PowerInteractorFactory.create().powerInteractor
- private val underTest =
- SceneContainerStartable(
- applicationScope = testScope.backgroundScope,
- sceneInteractor = sceneInteractor,
- deviceEntryInteractor = deviceEntryInteractor,
- keyguardInteractor = keyguardInteractor,
- flags = sceneContainerFlags,
- sysUiState = sysUiState,
- displayId = Display.DEFAULT_DISPLAY,
- sceneLogger = mock(),
- falsingCollector = falsingCollector,
- powerInteractor = powerInteractor,
- bouncerInteractor = bouncerInteractor,
- simBouncerInteractor = dagger.Lazy { utils.simBouncerInteractor },
- authenticationInteractor = dagger.Lazy { authenticationInteractor },
- )
+ private lateinit var underTest: SceneContainerStartable
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest =
+ SceneContainerStartable(
+ applicationScope = testScope.backgroundScope,
+ sceneInteractor = sceneInteractor,
+ deviceEntryInteractor = deviceEntryInteractor,
+ keyguardInteractor = keyguardInteractor,
+ flags = sceneContainerFlags,
+ sysUiState = sysUiState,
+ displayId = Display.DEFAULT_DISPLAY,
+ sceneLogger = mock(),
+ falsingCollector = falsingCollector,
+ powerInteractor = powerInteractor,
+ bouncerInteractor = bouncerInteractor,
+ simBouncerInteractor = dagger.Lazy { utils.simBouncerInteractor },
+ authenticationInteractor = dagger.Lazy { authenticationInteractor },
+ windowController = windowController,
+ )
+ }
@Test
fun hydrateVisibility() =
@@ -655,6 +670,58 @@
assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
}
+ @Test
+ fun hydrateWindowFocus() =
+ testScope.runTest {
+ val currentDesiredSceneKey by
+ collectLastValue(sceneInteractor.desiredScene.map { it.key })
+ val transitionStateFlow =
+ prepareState(
+ isDeviceUnlocked = true,
+ initialSceneKey = SceneKey.Gone,
+ )
+ assertThat(currentDesiredSceneKey).isEqualTo(SceneKey.Gone)
+ verify(windowController, never()).setNotificationShadeFocusable(anyBoolean())
+
+ underTest.start()
+ runCurrent()
+ verify(windowController, times(1)).setNotificationShadeFocusable(false)
+
+ sceneInteractor.changeScene(SceneModel(SceneKey.Shade), "reason")
+ transitionStateFlow.value =
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Gone,
+ toScene = SceneKey.Shade,
+ progress = flowOf(0.5f),
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ runCurrent()
+ verify(windowController, times(1)).setNotificationShadeFocusable(false)
+
+ sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason")
+ transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Shade)
+ runCurrent()
+ verify(windowController, times(1)).setNotificationShadeFocusable(true)
+
+ sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason")
+ transitionStateFlow.value =
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Shade,
+ toScene = SceneKey.Gone,
+ progress = flowOf(0.5f),
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ runCurrent()
+ verify(windowController, times(1)).setNotificationShadeFocusable(true)
+
+ sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason")
+ transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
+ runCurrent()
+ verify(windowController, times(2)).setNotificationShadeFocusable(false)
+ }
+
private fun TestScope.prepareState(
isDeviceUnlocked: Boolean = false,
isBypassEnabled: Boolean = false,
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index 1f671ac..f1017d8 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -40,6 +40,9 @@
<!-- Whether to show bottom sheets edge to edge -->
<bool name="config_edgeToEdgeBottomSheetDialog">false</bool>
+ <!-- Flag to activate drag notification to contents feature -->
+ <bool name="config_notificationToContents">true</bool>
+
<!-- A collection of defaults for the quick affordances on the lock screen. Each item must be a
string with two parts: the ID of the slot and the comma-delimited list of affordance IDs,
separated by a colon ':' character. For example: <item>bottom_end:home,wallet</item>. The
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e7eb984..854bb0f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3262,6 +3262,9 @@
<!-- Label for a button that, when clicked, sends the user to the app store to install an app. [CHAR LIMIT=64]. -->
<string name="install_app">Install app</string>
+ <!-- Instructions informing the user they can swipe up on the lockscreen to dismiss [CHAR LIMIT=48]-->
+ <string name="dismissible_keyguard_swipe">Swipe to continue</string>
+
<!--- Title of the dialog appearing when an external display is connected, asking whether to start mirroring [CHAR LIMIT=NONE]-->
<string name="connected_display_dialog_start_mirroring">Mirror to external display?</string>
<!--- Body of the mirroring dialog, shown when dual display is enabled. This signals that enabling mirroring will stop concurrent displays on a foldable device. [CHAR LIMIT=NONE]-->
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 2b41178..3a26ebf 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -66,6 +66,7 @@
"kotlinx_coroutines",
"dagger2",
"jsr330",
+ "com_android_systemui_shared_flags_lib",
],
resource_dirs: [
"res",
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 131eb6b..c08b083 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -20,10 +20,11 @@
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+import static com.android.systemui.shared.Flags.shadeAllowBackGesture;
+
import android.annotation.IntDef;
import android.content.Context;
import android.content.res.Resources;
-import android.os.SystemProperties;
import android.view.ViewConfiguration;
import android.view.WindowManagerPolicyConstants;
@@ -132,8 +133,7 @@
SYSUI_STATE_WAKEFULNESS_TRANSITION | SYSUI_STATE_AWAKE;
// Whether the back gesture is allowed (or ignored) by the Shade
- public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = SystemProperties.getBoolean(
- "persist.wm.debug.shade_allow_back_gesture", false);
+ public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = shadeAllowBackGesture();
@Retention(RetentionPolicy.SOURCE)
@IntDef({SYSUI_STATE_SCREEN_PINNING,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index a8bf229..05eeac6f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -133,7 +133,8 @@
static final int BOUNCER_DISMISS_EXTENDED_ACCESS = 3;
// Bouncer is dismissed due to sim card unlock code entered.
static final int BOUNCER_DISMISS_SIM = 4;
-
+ // Bouncer dismissed after being allowed to dismiss by forceDismissiblekeyguard
+ static final int BOUNCER_DISMISSIBLE_KEYGUARD = 5;
private static final String TAG = "KeyguardSecurityView";
// Make the view move slower than the finger, as if the spring were applying force.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 5e35e77..e457ca1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -21,6 +21,7 @@
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_DISMISSIBLE_KEYGUARD;
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;
@@ -78,10 +79,10 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.classifier.FalsingA11yDelegate;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.ActivityStarter;
@@ -135,7 +136,7 @@
private final SessionTracker mSessionTracker;
private final Optional<SideFpsController> mSideFpsController;
private final FalsingA11yDelegate mFalsingA11yDelegate;
- private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+ private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
private final BouncerMessageInteractor mBouncerMessageInteractor;
private int mTranslationY;
private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -216,7 +217,7 @@
@Override
public void onUserInput() {
mBouncerMessageInteractor.onPrimaryBouncerUserInput();
- mKeyguardFaceAuthInteractor.onPrimaryBouncerUserInput();
+ mDeviceEntryFaceAuthInteractor.onPrimaryBouncerUserInput();
}
@Override
@@ -347,11 +348,11 @@
private final SwipeListener mSwipeListener = new SwipeListener() {
@Override
public void onSwipeUp() {
- if (mKeyguardFaceAuthInteractor.canFaceAuthRun()) {
+ if (mDeviceEntryFaceAuthInteractor.canFaceAuthRun()) {
mKeyguardSecurityCallback.userActivity();
}
- mKeyguardFaceAuthInteractor.onSwipeUpOnBouncer();
- if (mKeyguardFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()) {
+ mDeviceEntryFaceAuthInteractor.onSwipeUpOnBouncer();
+ if (mDeviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()) {
mUpdateMonitor.requestActiveUnlock(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
"swipeUpOnBouncer");
@@ -456,7 +457,7 @@
TelephonyManager telephonyManager,
ViewMediatorCallback viewMediatorCallback,
AudioManager audioManager,
- KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
+ DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
BouncerMessageInteractor bouncerMessageInteractor,
Provider<JavaAdapter> javaAdapter,
SelectedUserInteractor selectedUserInteractor,
@@ -495,7 +496,7 @@
mTelephonyManager = telephonyManager;
mViewMediatorCallback = viewMediatorCallback;
mAudioManager = audioManager;
- mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+ mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
mBouncerMessageInteractor = bouncerMessageInteractor;
mSelectedUserInteractor = selectedUserInteractor;
mDeviceEntryInteractor = deviceEntryInteractor;
@@ -862,7 +863,11 @@
boolean finish = false;
int eventSubtype = -1;
BouncerUiEvent uiEvent = BouncerUiEvent.UNKNOWN;
- if (mUpdateMonitor.getUserHasTrust(targetUserId)) {
+ if (mUpdateMonitor.forceIsDismissibleIsKeepingDeviceUnlocked()) {
+ finish = true;
+ eventSubtype = BOUNCER_DISMISSIBLE_KEYGUARD;
+ // TODO: b/308417021 add UI event
+ } else if (mUpdateMonitor.getUserHasTrust(targetUserId)) {
finish = true;
eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS;
uiEvent = BouncerUiEvent.BOUNCER_DISMISS_EXTENDED_ACCESS;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 4d84d0b..fe96099 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -102,6 +102,7 @@
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
@@ -113,24 +114,26 @@
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.CoreStartable;
import com.android.systemui.Dumpable;
+import com.android.systemui.Flags;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
+import com.android.systemui.deviceentry.domain.interactor.FaceAuthenticationListener;
+import com.android.systemui.deviceentry.shared.model.AcquiredFaceAuthenticationStatus;
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus;
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus;
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus;
+import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus;
+import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus;
+import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.DumpsysTableLogger;
-import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.shared.constants.TrustAgentUiEvent;
-import com.android.systemui.keyguard.shared.model.AcquiredFaceAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus;
-import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.clocks.WeatherData;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -306,6 +309,10 @@
private boolean mKeyguardOccluded;
private boolean mCredentialAttempted;
private boolean mKeyguardGoingAway;
+ /**
+ * Whether the keyguard is forced into a dismissible state.
+ */
+ private boolean mForceIsDismissible;
private boolean mGoingToSleep;
private boolean mPrimaryBouncerFullyShown;
private boolean mPrimaryBouncerIsOrWillBeShowing;
@@ -359,7 +366,10 @@
@Nullable
private final BiometricManager mBiometricManager;
@Nullable
- private KeyguardFaceAuthInteractor mFaceAuthInteractor;
+ private DeviceEntryFaceAuthInteractor mFaceAuthInteractor;
+ @VisibleForTesting
+ protected FoldGracePeriodProvider mFoldGracePeriodProvider =
+ new FoldGracePeriodProvider();
private final DevicePostureController mDevicePostureController;
private final TaskStackChangeListeners mTaskStackChangeListeners;
private final IActivityTaskManager mActivityTaskManager;
@@ -373,6 +383,7 @@
private List<SubscriptionInfo> mSubscriptionInfo;
@VisibleForTesting
protected int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+ private boolean mFingerprintDetectRunning;
private boolean mIsDreaming;
private boolean mLogoutEnabled;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -725,6 +736,14 @@
}
/**
+ * Updates KeyguardUpdateMonitor's internal state to know the device should remain unlocked
+ * until the next signal to lock. Does nothing if the keyguard is already showing.
+ */
+ public void tryForceIsDismissibleKeyguard() {
+ setForceIsDismissibleKeyguard(true);
+ }
+
+ /**
* Updates KeyguardUpdateMonitor's internal state to know if keyguard is going away.
*/
public void setKeyguardGoingAway(boolean goingAway) {
@@ -986,6 +1005,7 @@
final boolean wasCancellingRestarting = mFingerprintRunningState
== BIOMETRIC_STATE_CANCELLING_RESTARTING;
mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+ mFingerprintDetectRunning = false;
if (wasCancellingRestarting) {
KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
} else {
@@ -1094,6 +1114,9 @@
boolean wasRunning = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING;
boolean isRunning = fingerprintRunningState == BIOMETRIC_STATE_RUNNING;
mFingerprintRunningState = fingerprintRunningState;
+ if (mFingerprintRunningState == BIOMETRIC_STATE_STOPPED) {
+ mFingerprintDetectRunning = false;
+ }
mLogger.logFingerprintRunningState(mFingerprintRunningState);
// Clients of KeyguardUpdateMonitor don't care about the internal state about the
// asynchronousness of the cancel cycle. So only notify them if the actually running state
@@ -1266,14 +1289,14 @@
return getFaceAuthInteractor() != null && getFaceAuthInteractor().isRunning();
}
- private @Nullable KeyguardFaceAuthInteractor getFaceAuthInteractor() {
+ private @Nullable DeviceEntryFaceAuthInteractor getFaceAuthInteractor() {
return mFaceAuthInteractor;
}
/**
* Set the face auth interactor that should be used for initiating face authentication.
*/
- public void setFaceAuthInteractor(KeyguardFaceAuthInteractor faceAuthInteractor) {
+ public void setFaceAuthInteractor(DeviceEntryFaceAuthInteractor faceAuthInteractor) {
if (mFaceAuthInteractor != null) {
mFaceAuthInteractor.unregisterListener(mFaceAuthenticationListener);
}
@@ -1356,7 +1379,7 @@
* @return whether the current user has been authenticated with face. This may be true
* on the lockscreen if the user doesn't have bypass enabled.
*
- * @deprecated Use {@link KeyguardFaceAuthInteractor#isAuthenticated()}
+ * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isAuthenticated()}
*/
@Deprecated
public boolean getIsFaceAuthenticated() {
@@ -1364,7 +1387,18 @@
}
public boolean getUserCanSkipBouncer(int userId) {
- return getUserHasTrust(userId) || getUserUnlockedWithBiometric(userId);
+ return getUserHasTrust(userId) || getUserUnlockedWithBiometric(userId)
+ || forceIsDismissibleIsKeepingDeviceUnlocked();
+ }
+
+ /**
+ * Whether the keyguard should be kept unlocked for the folding grace period.
+ */
+ public boolean forceIsDismissibleIsKeepingDeviceUnlocked() {
+ if (mFoldGracePeriodProvider.isEnabled()) {
+ return mForceIsDismissible && isUnlockingWithForceKeyguardDismissibleAllowed();
+ }
+ return false;
}
public boolean getUserHasTrust(int userId) {
@@ -1387,7 +1421,7 @@
/**
* Returns whether the user is unlocked with face.
- * @deprecated Use {@link KeyguardFaceAuthInteractor#isAuthenticated()} instead
+ * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isAuthenticated()} instead
*/
@Deprecated
public boolean isCurrentUserUnlockedWithFace() {
@@ -1468,6 +1502,10 @@
return isUnlockingWithBiometricAllowed(true);
}
+ private boolean isUnlockingWithForceKeyguardDismissibleAllowed() {
+ return isUnlockingWithBiometricAllowed(false);
+ }
+
public boolean isUnlockingWithBiometricAllowed(boolean isStrongBiometric) {
// StrongAuthTracker#isUnlockingWithBiometricAllowed includes
// STRONG_AUTH_REQUIRED_AFTER_LOCKOUT which is the same as mFingerprintLockedOutPermanent;
@@ -1979,6 +2017,7 @@
protected void handleStartedGoingToSleep(int arg1) {
Assert.isMainThread();
+ setForceIsDismissibleKeyguard(false);
clearFingerprintRecognized();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -2066,6 +2105,7 @@
@VisibleForTesting
void resetBiometricListeningState() {
mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+ mFingerprintDetectRunning = false;
}
@VisibleForTesting
@@ -2457,7 +2497,7 @@
/**
* @return true if there's at least one face enrolled
- * @deprecated Use {@link KeyguardFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()}
+ * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()}
*/
@Deprecated
public boolean isFaceEnabledAndEnrolled() {
@@ -2504,8 +2544,10 @@
return;
}
final boolean shouldListenForFingerprint = shouldListenForFingerprint(isUdfpsSupported());
- final boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
+ final boolean running = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING;
+ final boolean runningOrRestarting = running
|| mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING;
+ final boolean runDetect = shouldRunFingerprintDetect();
if (runningOrRestarting && !shouldListenForFingerprint) {
if (action == BIOMETRIC_ACTION_START) {
mLogger.v("Ignoring stopListeningForFingerprint()");
@@ -2517,10 +2559,24 @@
mLogger.v("Ignoring startListeningForFingerprint()");
return;
}
- startListeningForFingerprint();
+ startListeningForFingerprint(runDetect);
+ } else if (running && runDetect && !mFingerprintDetectRunning) {
+ if (action == BIOMETRIC_ACTION_STOP) {
+ mLogger.v("Ignoring startListeningForFingerprint(detect)");
+ return;
+ }
+ // stop running authentication and start running fingerprint detection
+ stopListeningForFingerprint();
+ startListeningForFingerprint(true);
}
}
+ private boolean shouldRunFingerprintDetect() {
+ return !isUnlockingWithFingerprintAllowed()
+ || (Flags.runFingerprintDetectOnDismissibleKeyguard()
+ && getUserCanSkipBouncer(mSelectedUserInteractor.getSelectedUserId()));
+ }
+
/**
* If a user is encrypted or not.
* This is NOT related to the lock screen being visible or not.
@@ -2776,7 +2832,6 @@
&& biometricEnabledForUser
&& !isUserInLockdown(user);
final boolean strongerAuthRequired = !isUnlockingWithFingerprintAllowed();
- final boolean isSideFps = isSfpsSupported() && isSfpsEnrolled();
final boolean shouldListenBouncerState =
!strongerAuthRequired || !mPrimaryBouncerIsOrWillBeShowing;
@@ -2822,7 +2877,7 @@
/**
* If face auth is allows to scan on this exact moment.
*
- * @deprecated Use {@link KeyguardFaceAuthInteractor#canFaceAuthRun()}
+ * @deprecated Use {@link DeviceEntryFaceAuthInteractor#canFaceAuthRun()}
*/
@Deprecated
public boolean shouldListenForFace() {
@@ -2839,7 +2894,7 @@
}
}
- private void startListeningForFingerprint() {
+ private void startListeningForFingerprint(boolean runDetect) {
final int userId = mSelectedUserInteractor.getSelectedUserId();
final boolean unlockPossible = isUnlockWithFingerprintPossible(userId);
if (mFingerprintCancelSignal != null) {
@@ -2869,18 +2924,20 @@
mFingerprintInteractiveToAuthProvider.getVendorExtension(userId));
}
- if (!isUnlockingWithFingerprintAllowed()) {
+ if (runDetect) {
mLogger.v("startListeningForFingerprint - detect");
mFpm.detectFingerprint(
mFingerprintCancelSignal,
mFingerprintDetectionCallback,
fingerprintAuthenticateOptions);
+ mFingerprintDetectRunning = true;
} else {
mLogger.v("startListeningForFingerprint");
mFpm.authenticate(null /* crypto */, mFingerprintCancelSignal,
mFingerprintAuthenticationCallback,
null /* handler */,
fingerprintAuthenticateOptions);
+ mFingerprintDetectRunning = false;
}
setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
}
@@ -2891,7 +2948,7 @@
}
/**
- * @deprecated Use {@link KeyguardFaceAuthInteractor#isLockedOut()}
+ * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isLockedOut()}
*/
@Deprecated
public boolean isFaceLockedOut() {
@@ -2932,7 +2989,7 @@
}
/**
- * @deprecated Use {@link KeyguardFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()}
+ * @deprecated Use {@link DeviceEntryFaceAuthInteractor#isFaceAuthEnabledAndEnrolled()}
*/
@VisibleForTesting
@Deprecated
@@ -3033,6 +3090,7 @@
void handleUserSwitching(int userId, Runnable resultCallback) {
mLogger.logUserSwitching(userId, "from UserTracker");
Assert.isMainThread();
+ setForceIsDismissibleKeyguard(false);
clearFingerprintRecognized();
boolean trustUsuallyManaged = mTrustManager.isTrustUsuallyManaged(userId);
mLogger.logTrustUsuallyManagedUpdated(userId, mUserTrustIsUsuallyManaged.get(userId),
@@ -3611,6 +3669,31 @@
}
}
+ private void setForceIsDismissibleKeyguard(boolean forceIsDismissible) {
+ Assert.isMainThread();
+ if (!mFoldGracePeriodProvider.isEnabled()) {
+ // never send updates if the feature isn't enabled
+ return;
+ }
+ if (mKeyguardShowing && forceIsDismissible) {
+ // never keep the device unlocked if the keyguard was already showing
+ mLogger.d("Skip setting forceIsDismissibleKeyguard to true. "
+ + "Keyguard already showing.");
+ return;
+ }
+ if (mForceIsDismissible != forceIsDismissible) {
+ mForceIsDismissible = forceIsDismissible;
+ mLogger.logForceIsDismissibleKeyguard(mForceIsDismissible);
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onForceIsDismissibleChanged(forceIsDismissibleIsKeepingDeviceUnlocked());
+ }
+ }
+ }
+
+ }
+
public boolean isSimPinVoiceSecure() {
// TODO: only count SIMs that handle voice
return isSimPinSecure();
@@ -3872,10 +3955,14 @@
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("KeyguardUpdateMonitor state:");
+ pw.println(" forceIsDismissible=" + mForceIsDismissible);
+ pw.println(" forceIsDismissibleIsKeepingDeviceUnlocked="
+ + forceIsDismissibleIsKeepingDeviceUnlocked());
pw.println(" getUserHasTrust()=" + getUserHasTrust(
mSelectedUserInteractor.getSelectedUserId()));
pw.println(" getUserUnlockedWithBiometric()="
+ getUserUnlockedWithBiometric(mSelectedUserInteractor.getSelectedUserId()));
+ pw.println(" mFingerprintDetectRunning=" + mFingerprintDetectRunning);
pw.println(" SIM States:");
for (SimData data : mSimDatas.values()) {
pw.println(" " + data.toString());
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 9d216dce..7ac5ac2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -339,4 +339,9 @@
* On biometric enrollment state changed
*/
public void onBiometricEnrollmentStateChanged(BiometricSourceType biometricSourceType) { }
+
+ /**
+ * On force is dismissible state changed.
+ */
+ public void onForceIsDismissibleChanged(boolean forceIsDismissible) { }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 055ca56..1f4e732 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -639,4 +639,12 @@
{ "fingerprint acquire message: $int1" }
)
}
+ fun logForceIsDismissibleKeyguard(keepUnlocked: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { bool1 = keepUnlocked },
+ { "keepUnlockedOnFold changed to: $bool1" }
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
index 066cba23..6076f32 100644
--- a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
@@ -22,10 +22,9 @@
import android.window.OnBackInvokedDispatcher
import android.window.WindowOnBackInvokedDispatcher
import com.android.systemui.CoreStartable
+import com.android.systemui.Flags.predictiveBackAnimateShade
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
import com.android.systemui.shade.QuickSettingsController
@@ -48,14 +47,13 @@
private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
private val shadeController: ShadeController,
private val notificationShadeWindowController: NotificationShadeWindowController,
- private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor,
- featureFlags: FeatureFlags,
+ private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor
) : CoreStartable {
private var isCallbackRegistered = false
private val callback =
- if (featureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE)) {
+ if (predictiveBackAnimateShade()) {
/**
* New callback that handles back gesture invoked, cancel, progress and provides
* feedback via Shade animation.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 6345312..45967c6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -32,13 +32,13 @@
import com.android.settingslib.Utils
import com.android.systemui.CoreStartable
import com.android.systemui.Flags.lightRevealMigration
-import com.android.systemui.res.R
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.log.core.LogLevel
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LiftReveal
import com.android.systemui.statusbar.LightRevealEffect
@@ -50,6 +50,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.ViewController
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import java.io.PrintWriter
import javax.inject.Inject
import javax.inject.Provider
@@ -62,6 +63,7 @@
*
* The ripple uses the accent color of the current theme.
*/
+@ExperimentalCoroutinesApi
@SysUISingleton
class AuthRippleController @Inject constructor(
private val sysuiContext: Context,
@@ -75,7 +77,6 @@
private val udfpsControllerProvider: Provider<UdfpsController>,
private val statusBarStateController: StatusBarStateController,
private val displayMetrics: DisplayMetrics,
- private val featureFlags: FeatureFlags,
private val logger: KeyguardLogger,
private val biometricUnlockController: BiometricUnlockController,
private val lightRevealScrim: LightRevealScrim,
@@ -313,6 +314,18 @@
mView.fadeDwellRipple()
}
}
+
+ override fun onBiometricDetected(
+ userId: Int,
+ biometricSourceType: BiometricSourceType,
+ isStrongBiometric: Boolean
+ ) {
+ // TODO (b/309804148): add support detect auth ripple for deviceEntryUdfpsRefactor
+ if (!DeviceEntryUdfpsRefactor.isEnabled &&
+ keyguardUpdateMonitor.getUserCanSkipBouncer(userId)) {
+ showUnlockRipple(biometricSourceType)
+ }
+ }
}
private val configurationChangedListener =
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt
index cb75049..3ea1632 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegate.kt
@@ -22,7 +22,7 @@
import android.view.accessibility.AccessibilityNodeInfo
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.res.R
import javax.inject.Inject
@@ -35,7 +35,7 @@
@Inject
constructor(
@Main private val resources: Resources,
- private val faceAuthInteractor: KeyguardFaceAuthInteractor,
+ private val faceAuthInteractor: DeviceEntryFaceAuthInteractor,
) : View.AccessibilityDelegate() {
override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) {
super.onInitializeAccessibilityNodeInfo(host, info)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 2fd13b3..d664637 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -80,11 +80,11 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
@@ -149,7 +149,7 @@
@NonNull private final DumpManager mDumpManager;
@NonNull private final SystemUIDialogManager mDialogManager;
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @NonNull private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+ @NonNull private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
@NonNull private final VibratorHelper mVibrator;
@NonNull private final FalsingManager mFalsingManager;
@NonNull private final PowerManager mPowerManager;
@@ -664,7 +664,7 @@
@NonNull SessionTracker sessionTracker,
@NonNull AlternateBouncerInteractor alternateBouncerInteractor,
@NonNull InputManager inputManager,
- @NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
+ @NonNull DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
@NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate,
@NonNull SelectedUserInteractor selectedUserInteractor,
@NonNull FpsUnlockTracker fpsUnlockTracker,
@@ -736,7 +736,7 @@
}
return Unit.INSTANCE;
});
- mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+ mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
final UdfpsOverlayController mUdfpsOverlayController = new UdfpsOverlayController();
mFingerprintManager.setUdfpsOverlayController(mUdfpsOverlayController);
@@ -1027,7 +1027,7 @@
if (!mOnFingerDown) {
playStartHaptic();
- mKeyguardFaceAuthInteractor.onUdfpsSensorTouched();
+ mDeviceEntryFaceAuthInteractor.onUdfpsSensorTouched();
}
mOnFingerDown = true;
mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, pointerId, x, y,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index 81d822f..c8ce245 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -29,7 +29,7 @@
import com.android.systemui.classifier.domain.interactor.FalsingInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.res.R
import javax.inject.Inject
@@ -52,7 +52,7 @@
@Application private val applicationContext: Context,
private val repository: BouncerRepository,
private val authenticationInteractor: AuthenticationInteractor,
- private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor,
+ private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
private val falsingInteractor: FalsingInteractor,
private val powerInteractor: PowerInteractor,
private val simBouncerInteractor: SimBouncerInteractor,
@@ -100,7 +100,7 @@
* user's pocket or by the user's face while holding their device up to their ear.
*/
fun onIntentionalUserInput() {
- keyguardFaceAuthInteractor.onPrimaryBouncerUserInput()
+ deviceEntryFaceAuthInteractor.onPrimaryBouncerUserInput()
powerInteractor.onUserTouch()
falsingInteractor.updateFalseConfidence(FalsingClassifier.Result.passed(0.6))
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
index b587ed8..ef4554c 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
@@ -30,9 +30,9 @@
import com.android.systemui.bouncer.shared.model.Message
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.flags.SystemPropertiesHelper
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.TrustRepository
import com.android.systemui.res.R.string.bouncer_face_not_recognized
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index 621ca5d..654fa22 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -38,9 +38,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.TrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.shared.system.SysUiStatsLog
@@ -77,7 +77,7 @@
private val trustRepository: TrustRepository,
@Application private val applicationScope: CoroutineScope,
private val selectedUserInteractor: SelectedUserInteractor,
- private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor,
+ private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
) {
private val passiveAuthBouncerDelay =
context.resources.getInteger(R.integer.primary_bouncer_passive_auth_delay).toLong()
@@ -429,7 +429,7 @@
keyguardUpdateMonitor.canTriggerActiveUnlockBasedOnDeviceState()
return !needsFullscreenBouncer() &&
- (keyguardFaceAuthInteractor.canFaceAuthRun() || canRunActiveUnlock)
+ (deviceEntryFaceAuthInteractor.canFaceAuthRun() || canRunActiveUnlock)
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index 577e404..c34a8df 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -20,6 +20,7 @@
import android.os.PowerManager
import android.os.SystemClock
import android.view.MotionEvent
+import android.widget.RemoteViews
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalSceneKey
@@ -101,4 +102,7 @@
/** Called as the UI requests opening the widget editor. */
open fun onOpenWidgetEditor() {}
+
+ /** Gets the interaction handler used to handle taps on a remote view */
+ abstract fun getInteractionHandler(): RemoteViews.InteractionHandler
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index 368df9e..da7bd34 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.communal.ui.viewmodel
import android.os.PowerManager
+import android.widget.RemoteViews
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.dagger.SysUISingleton
@@ -49,4 +50,9 @@
override fun onReorderWidgets(widgetIdToPriorityMap: Map<Int, Int>) =
communalInteractor.updateWidgetOrder(widgetIdToPriorityMap)
+
+ override fun getInteractionHandler(): RemoteViews.InteractionHandler {
+ // Ignore all interactions in edit mode.
+ return RemoteViews.InteractionHandler { _, _, _ -> false }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index abf1986..2fae8b5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -17,9 +17,11 @@
package com.android.systemui.communal.ui.viewmodel
import android.os.PowerManager
+import android.widget.RemoteViews
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.widgets.WidgetInteractionHandler
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.media.controls.ui.MediaHost
import com.android.systemui.media.dagger.MediaModule
@@ -39,6 +41,7 @@
@Inject
constructor(
private val communalInteractor: CommunalInteractor,
+ private val interactionHandler: WidgetInteractionHandler,
tutorialInteractor: CommunalTutorialInteractor,
shadeViewController: Provider<ShadeViewController>,
powerManager: PowerManager,
@@ -60,4 +63,6 @@
}
override fun onOpenWidgetEditor() = communalInteractor.showWidgetEditor()
+
+ override fun getInteractionHandler(): RemoteViews.InteractionHandler = interactionHandler
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index c936c63..0f94a92 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -23,6 +23,7 @@
import android.os.RemoteException
import android.util.Log
import android.view.IWindowManager
+import android.view.WindowInsets
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
@@ -79,6 +80,10 @@
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ val windowInsetsController = window.decorView.windowInsetsController
+ windowInsetsController?.hide(WindowInsets.Type.systemBars())
+ window.setDecorFitsSystemWindows(false)
+
setCommunalEditWidgetActivityContent(
activity = this,
viewModel = communalViewModel,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
new file mode 100644
index 0000000..c8db70b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 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.communal.widgets
+
+import android.app.PendingIntent
+import android.view.View
+import android.widget.RemoteViews
+import com.android.systemui.plugins.ActivityStarter
+import javax.inject.Inject
+
+class WidgetInteractionHandler
+@Inject
+constructor(
+ private val activityStarter: ActivityStarter,
+) : RemoteViews.InteractionHandler {
+ override fun onInteraction(
+ view: View,
+ pendingIntent: PendingIntent,
+ response: RemoteViews.RemoteResponse
+ ): Boolean =
+ when {
+ pendingIntent.isActivity -> startActivity(pendingIntent)
+ else ->
+ RemoteViews.startPendingIntent(view, pendingIntent, response.getLaunchOptions(view))
+ }
+
+ private fun startActivity(pendingIntent: PendingIntent): Boolean {
+ activityStarter.startPendingIntentMaybeDismissingKeyguard(
+ /* intent = */ pendingIntent,
+ /* intentSentUiThreadCallback = */ null,
+ // TODO(b/318758390): Properly animate activities started from widgets.
+ /* animationController = */ null
+ )
+ return true
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index e71007b..8831dc6 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -484,7 +484,9 @@
}
},
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
- null,
+ ActivityOptions.makeBasic()
+ .setPendingIntentCreatorBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle(),
userTracker.userHandle
)
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
index b915418..71b5ab2 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
@@ -1,6 +1,7 @@
package com.android.systemui.deviceentry
import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule
+import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigModule
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import dagger.Module
import dagger.multibindings.Multibinds
@@ -9,6 +10,7 @@
includes =
[
DeviceEntryRepositoryModule::class,
+ FaceWakeUpTriggersConfigModule::class,
],
)
abstract class DeviceEntryModule {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
rename to packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
index 17d7836..7a70c4a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.keyguard.data.repository
+package com.android.systemui.deviceentry.data.repository
import android.app.StatusBarManager
import android.content.Context
@@ -22,7 +22,6 @@
import android.os.CancellationSignal
import com.android.internal.logging.InstanceId
import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.FaceAuthUiEvent
import com.android.systemui.Dumpable
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
@@ -32,19 +31,27 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.model.AcquiredFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
+import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.BiometricType
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FaceAuthTableLog
+import com.android.systemui.keyguard.data.repository.FaceDetectTableLog
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.data.repository.TrustRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.AcquiredFaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
-import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.log.FaceAuthenticationLogger
diff --git a/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfig.kt
similarity index 83%
rename from packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt
rename to packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfig.kt
index 84a6b09..9d6718b 100644
--- a/packages/SystemUI/src/com/android/keyguard/FaceWakeUpTriggersConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfig.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,28 +14,35 @@
* limitations under the License.
*/
-package com.android.keyguard
+package com.android.systemui.deviceentry.data.repository
import android.content.res.Resources
import android.os.Build
import android.os.PowerManager
import com.android.systemui.Dumpable
-import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.res.R
import com.android.systemui.util.settings.GlobalSettings
+import dagger.Binds
+import dagger.Module
import java.io.PrintWriter
import java.util.stream.Collectors
import javax.inject.Inject
/** Determines which device wake-ups should trigger passive authentication. */
+interface FaceWakeUpTriggersConfig {
+ fun shouldTriggerFaceAuthOnWakeUpFrom(@PowerManager.WakeReason pmWakeReason: Int): Boolean
+ fun shouldTriggerFaceAuthOnWakeUpFrom(wakeReason: WakeSleepReason): Boolean
+}
+
@SysUISingleton
-class FaceWakeUpTriggersConfig
+class FaceWakeUpTriggersConfigImpl
@Inject
constructor(@Main resources: Resources, globalSettings: GlobalSettings, dumpManager: DumpManager) :
- Dumpable {
+ Dumpable, FaceWakeUpTriggersConfig {
private val defaultTriggerFaceAuthOnWakeUpFrom: Set<Int> =
resources.getIntArray(R.array.config_face_auth_wake_up_triggers).toSet()
private val triggerFaceAuthOnWakeUpFrom: Set<Int>
@@ -65,11 +72,13 @@
dumpManager.registerDumpable(this)
}
- fun shouldTriggerFaceAuthOnWakeUpFrom(@PowerManager.WakeReason pmWakeReason: Int): Boolean {
+ override fun shouldTriggerFaceAuthOnWakeUpFrom(
+ @PowerManager.WakeReason pmWakeReason: Int
+ ): Boolean {
return triggerFaceAuthOnWakeUpFrom.contains(pmWakeReason)
}
- fun shouldTriggerFaceAuthOnWakeUpFrom(wakeReason: WakeSleepReason): Boolean =
+ override fun shouldTriggerFaceAuthOnWakeUpFrom(wakeReason: WakeSleepReason): Boolean =
wakeSleepReasonsToTriggerFaceAuth.contains(wakeReason)
override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -87,3 +96,8 @@
?: default
}
}
+
+@Module
+interface FaceWakeUpTriggersConfigModule {
+ @Binds fun repository(impl: FaceWakeUpTriggersConfigImpl): FaceWakeUpTriggersConfig
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/NoopDeviceEntryFaceAuthRepository.kt
similarity index 68%
rename from packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
rename to packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/NoopDeviceEntryFaceAuthRepository.kt
index f4a74f0..6695b182 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/NoopDeviceEntryFaceAuthRepository.kt
@@ -1,25 +1,25 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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
+ * 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
+ * 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.
+ * 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.keyguard.data.repository
+package com.android.systemui.deviceentry.data.repository
-import com.android.keyguard.FaceAuthUiEvent
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
index 1a6bd04..5699176 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
@@ -18,13 +18,14 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.shared.DeviceEntryBiometricMode
+import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
@@ -62,7 +63,9 @@
val faceOnlyFaceFailure: Flow<FailedFaceAuthenticationStatus> =
faceOnly.flatMapLatest { faceOnly ->
if (faceOnly) {
- deviceEntryFaceAuthInteractor.faceFailure
+ deviceEntryFaceAuthInteractor.authenticationStatus.filterIsInstance<
+ FailedFaceAuthenticationStatus
+ >()
} else {
emptyFlow()
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
index 70716c6..99bd25b 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -16,19 +16,80 @@
package com.android.systemui.deviceentry.domain.interactor
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
-import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
-import javax.inject.Inject
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.filterIsInstance
-@SysUISingleton
-class DeviceEntryFaceAuthInteractor
-@Inject
-constructor(
- repository: DeviceEntryFaceAuthRepository,
-) {
- val faceFailure: Flow<FailedFaceAuthenticationStatus> =
- repository.authenticationStatus.filterIsInstance<FailedFaceAuthenticationStatus>()
+/**
+ * Interactor that exposes API to get the face authentication status and handle any events that can
+ * cause face authentication to run for device entry.
+ */
+interface DeviceEntryFaceAuthInteractor {
+
+ /** Current authentication status */
+ val authenticationStatus: Flow<FaceAuthenticationStatus>
+
+ /** Current detection status */
+ val detectionStatus: Flow<FaceDetectionStatus>
+
+ /** Can face auth be run right now */
+ fun canFaceAuthRun(): Boolean
+
+ /** Whether face auth is currently running or not. */
+ fun isRunning(): Boolean
+
+ /** Whether face auth is in lock out state. */
+ fun isLockedOut(): Boolean
+
+ /** Whether face auth is enrolled and enabled for the current user */
+ fun isFaceAuthEnabledAndEnrolled(): Boolean
+
+ /** Whether the current user is authenticated successfully with face auth */
+ fun isAuthenticated(): Boolean
+ /**
+ * Register listener for use from code that cannot use [authenticationStatus] or
+ * [detectionStatus]
+ */
+ fun registerListener(listener: FaceAuthenticationListener)
+
+ /** Unregister previously registered listener */
+ fun unregisterListener(listener: FaceAuthenticationListener)
+
+ fun onUdfpsSensorTouched()
+ fun onAssistantTriggeredOnLockScreen()
+ fun onDeviceLifted()
+ fun onQsExpansionStared()
+ fun onNotificationPanelClicked()
+ fun onSwipeUpOnBouncer()
+ fun onPrimaryBouncerUserInput()
+ fun onAccessibilityAction()
+ fun onWalletLaunched()
+
+ /** Whether face auth is considered class 3 */
+ fun isFaceAuthStrong(): Boolean
+}
+
+/**
+ * Listener that can be registered with the [DeviceEntryFaceAuthInteractor] to receive updates about
+ * face authentication & detection updates.
+ *
+ * This is present to make it easier for use the new face auth API for code that cannot use
+ * [DeviceEntryFaceAuthInteractor.authenticationStatus] or
+ * [DeviceEntryFaceAuthInteractor.detectionStatus] flows.
+ */
+interface FaceAuthenticationListener {
+ /** Receive face isAuthenticated updates */
+ fun onAuthenticatedChanged(isAuthenticated: Boolean)
+
+ /** Receive face authentication status updates */
+ fun onAuthenticationStatusChanged(status: FaceAuthenticationStatus)
+
+ /** Receive status updates whenever face detection runs */
+ fun onDetectionStatusChanged(status: FaceDetectionStatus)
+
+ fun onLockoutStateChanged(isLockedOut: Boolean)
+
+ fun onRunningStateChanged(isRunning: Boolean)
+
+ fun onAuthEnrollmentStateChanged(enrolled: Boolean)
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index f6a9570..2680328 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -20,8 +20,8 @@
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.TrustRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.scene.domain.interactor.SceneInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
new file mode 100644
index 0000000..3b94166
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 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.deviceentry.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+
+/**
+ * Implementation of the interactor that noops all face auth operations.
+ *
+ * This is required for SystemUI variants that do not support face authentication but still inject
+ * other SysUI components that depend on [DeviceEntryFaceAuthInteractor]
+ */
+@SysUISingleton
+class NoopDeviceEntryFaceAuthInteractor @Inject constructor() : DeviceEntryFaceAuthInteractor {
+ override val authenticationStatus: Flow<FaceAuthenticationStatus>
+ get() = emptyFlow()
+ override val detectionStatus: Flow<FaceDetectionStatus>
+ get() = emptyFlow()
+
+ override fun canFaceAuthRun(): Boolean = false
+
+ override fun isRunning(): Boolean = false
+
+ override fun isLockedOut(): Boolean = false
+
+ override fun isFaceAuthEnabledAndEnrolled(): Boolean = false
+
+ override fun isFaceAuthStrong(): Boolean = false
+
+ override fun isAuthenticated(): Boolean = false
+
+ override fun registerListener(listener: FaceAuthenticationListener) {}
+
+ override fun unregisterListener(listener: FaceAuthenticationListener) {}
+
+ override fun onUdfpsSensorTouched() {}
+
+ override fun onAssistantTriggeredOnLockScreen() {}
+
+ override fun onDeviceLifted() {}
+
+ override fun onQsExpansionStared() {}
+
+ override fun onNotificationPanelClicked() {}
+
+ override fun onSwipeUpOnBouncer() {}
+ override fun onPrimaryBouncerUserInput() {}
+ override fun onAccessibilityAction() {}
+ override fun onWalletLaunched() = Unit
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
index e3f4739..98130eb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
@@ -14,14 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.keyguard.domain.interactor
+package com.android.systemui.deviceentry.domain.interactor
import android.app.trust.TrustManager
import android.content.Context
import android.hardware.biometrics.BiometricFaceConstants
import android.hardware.biometrics.BiometricSourceType
-import com.android.keyguard.FaceAuthUiEvent
-import com.android.keyguard.FaceWakeUpTriggersConfig
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.CoreStartable
import com.android.systemui.biometrics.data.repository.FacePropertyRepository
@@ -32,11 +30,14 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.log.FaceAuthenticationLogger
import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -65,7 +66,7 @@
* SystemUI Keyguard.
*/
@SysUISingleton
-class SystemUIKeyguardFaceAuthInteractor
+class SystemUIDeviceEntryFaceAuthInteractor
@Inject
constructor(
private val context: Context,
@@ -84,7 +85,7 @@
private val powerInteractor: PowerInteractor,
private val biometricSettingsRepository: BiometricSettingsRepository,
private val trustManager: TrustManager,
-) : CoreStartable, KeyguardFaceAuthInteractor {
+) : CoreStartable, DeviceEntryFaceAuthInteractor {
private val listeners: MutableList<FaceAuthenticationListener> = mutableListOf()
@@ -310,7 +311,7 @@
}
companion object {
- const val TAG = "KeyguardFaceAuthInteractor"
+ const val TAG = "DeviceEntryFaceAuthInteractor"
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
similarity index 73%
rename from packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
rename to packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
index 2abb7a4..ee220d5 100644
--- a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,58 +14,55 @@
* limitations under the License.
*/
-package com.android.keyguard
+package com.android.systemui.deviceentry.shared
import android.annotation.StringDef
import android.os.PowerManager
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.FaceAuthApiRequestReason.Companion.ACCESSIBILITY_ACTION
-import com.android.keyguard.FaceAuthApiRequestReason.Companion.NOTIFICATION_PANEL_CLICKED
-import com.android.keyguard.FaceAuthApiRequestReason.Companion.PICK_UP_GESTURE_TRIGGERED
-import com.android.keyguard.FaceAuthApiRequestReason.Companion.QS_EXPANDED
-import com.android.keyguard.FaceAuthApiRequestReason.Companion.SWIPE_UP_ON_BOUNCER
-import com.android.keyguard.FaceAuthApiRequestReason.Companion.UDFPS_POINTER_DOWN
-import com.android.keyguard.InternalFaceAuthReasons.ALL_AUTHENTICATORS_REGISTERED
-import com.android.keyguard.InternalFaceAuthReasons.ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
-import com.android.keyguard.InternalFaceAuthReasons.ASSISTANT_VISIBILITY_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.AUTH_REQUEST_DURING_CANCELLATION
-import com.android.keyguard.InternalFaceAuthReasons.BIOMETRIC_ENABLED
-import com.android.keyguard.InternalFaceAuthReasons.CAMERA_LAUNCHED
-import com.android.keyguard.InternalFaceAuthReasons.DEVICE_WOKEN_UP_ON_REACH_GESTURE
-import com.android.keyguard.InternalFaceAuthReasons.DISPLAY_OFF
-import com.android.keyguard.InternalFaceAuthReasons.DREAM_STARTED
-import com.android.keyguard.InternalFaceAuthReasons.DREAM_STOPPED
-import com.android.keyguard.InternalFaceAuthReasons.ENROLLMENTS_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.FACE_AUTHENTICATED
-import com.android.keyguard.InternalFaceAuthReasons.FACE_AUTH_STOPPED_ON_USER_INPUT
-import com.android.keyguard.InternalFaceAuthReasons.FACE_CANCEL_NOT_RECEIVED
-import com.android.keyguard.InternalFaceAuthReasons.FACE_LOCKOUT_RESET
-import com.android.keyguard.InternalFaceAuthReasons.FINISHED_GOING_TO_SLEEP
-import com.android.keyguard.InternalFaceAuthReasons.FP_AUTHENTICATED
-import com.android.keyguard.InternalFaceAuthReasons.FP_LOCKED_OUT
-import com.android.keyguard.InternalFaceAuthReasons.GOING_TO_SLEEP
-import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_GOING_AWAY
-import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_INIT
-import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_OCCLUSION_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_RESET
-import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_VISIBILITY_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.NON_STRONG_BIOMETRIC_ALLOWED_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.OCCLUDING_APP_REQUESTED
-import com.android.keyguard.InternalFaceAuthReasons.POSTURE_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN
-import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN
-import com.android.keyguard.InternalFaceAuthReasons.RETRY_AFTER_HW_UNAVAILABLE
-import com.android.keyguard.InternalFaceAuthReasons.STARTED_WAKING_UP
-import com.android.keyguard.InternalFaceAuthReasons.STRONG_AUTH_ALLOWED_CHANGED
-import com.android.keyguard.InternalFaceAuthReasons.TRUST_DISABLED
-import com.android.keyguard.InternalFaceAuthReasons.TRUST_ENABLED
-import com.android.keyguard.InternalFaceAuthReasons.USER_SWITCHING
+import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.ACCESSIBILITY_ACTION
+import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.NOTIFICATION_PANEL_CLICKED
+import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.PICK_UP_GESTURE_TRIGGERED
+import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.QS_EXPANDED
+import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.SWIPE_UP_ON_BOUNCER
+import com.android.systemui.deviceentry.shared.FaceAuthApiRequestReason.Companion.UDFPS_POINTER_DOWN
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ALL_AUTHENTICATORS_REGISTERED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ASSISTANT_VISIBILITY_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.AUTH_REQUEST_DURING_CANCELLATION
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.BIOMETRIC_ENABLED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.CAMERA_LAUNCHED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DEVICE_WOKEN_UP_ON_REACH_GESTURE
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DISPLAY_OFF
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DREAM_STARTED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.DREAM_STOPPED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.ENROLLMENTS_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FACE_AUTHENTICATED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FACE_AUTH_STOPPED_ON_USER_INPUT
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FACE_CANCEL_NOT_RECEIVED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FACE_LOCKOUT_RESET
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FINISHED_GOING_TO_SLEEP
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FP_AUTHENTICATED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.FP_LOCKED_OUT
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.GOING_TO_SLEEP
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_GOING_AWAY
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_INIT
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_OCCLUSION_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_RESET
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.KEYGUARD_VISIBILITY_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.NON_STRONG_BIOMETRIC_ALLOWED_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.OCCLUDING_APP_REQUESTED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.POSTURE_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.RETRY_AFTER_HW_UNAVAILABLE
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.STARTED_WAKING_UP
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.STRONG_AUTH_ALLOWED_CHANGED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.TRUST_DISABLED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.TRUST_ENABLED
+import com.android.systemui.deviceentry.shared.InternalFaceAuthReasons.USER_SWITCHING
-/**
- * List of reasons why face auth is requested by clients through
- * [KeyguardUpdateMonitor.requestFaceAuth].
- */
+/** List of reasons why face auth is requested by clients. */
@Retention(AnnotationRetention.SOURCE)
@StringDef(
SWIPE_UP_ON_BOUNCER,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/FaceAuthenticationModels.kt
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
rename to packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/FaceAuthenticationModels.kt
index 3de3666..f006b34 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/FaceAuthenticationModels.kt
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.keyguard.shared.model
+package com.android.systemui.deviceentry.shared.model
import android.hardware.face.FaceManager
import android.os.SystemClock.elapsedRealtime
/**
* Authentication status provided by
- * [com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository]
+ * [com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository]
*/
sealed class FaceAuthenticationStatus
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index bb0c273..38c7c6a 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -444,29 +444,10 @@
// TODO(b/254512728): Tracking Bug
@JvmField val NEW_BACK_AFFORDANCE = releasedFlag("new_back_affordance")
- // TODO(b/255854141): Tracking Bug
- @JvmField
- val WM_ENABLE_PREDICTIVE_BACK_SYSUI =
- unreleasedFlag("persist.wm.debug.predictive_back_sysui_enable", teamfood = true)
// TODO(b/270987164): Tracking Bug
@JvmField val TRACKPAD_GESTURE_FEATURES = releasedFlag("trackpad_gesture_features")
- // TODO(b/263826204): Tracking Bug
- @JvmField
- val WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM =
- unreleasedFlag("persist.wm.debug.predictive_back_bouncer_anim", teamfood = true)
-
- // TODO(b/238475428): Tracking Bug
- @JvmField
- val WM_SHADE_ALLOW_BACK_GESTURE =
- sysPropBooleanFlag("persist.wm.debug.shade_allow_back_gesture", default = false)
-
- // TODO(b/238475428): Tracking Bug
- @JvmField
- val WM_SHADE_ANIMATE_BACK_GESTURE =
- unreleasedFlag("persist.wm.debug.shade_animate_back_gesture", teamfood = false)
-
// TODO(b/265639042): Tracking Bug
@JvmField
val WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
index aa4c88a..e23ec89 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
@@ -403,6 +403,7 @@
public static final int INDICATION_TYPE_REVERSE_CHARGING = 10;
public static final int INDICATION_TYPE_BIOMETRIC_MESSAGE = 11;
public static final int INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP = 12;
+ public static final int INDICATION_IS_DISMISSIBLE = 13;
@IntDef({
INDICATION_TYPE_NONE,
@@ -417,7 +418,8 @@
INDICATION_TYPE_USER_LOCKED,
INDICATION_TYPE_REVERSE_CHARGING,
INDICATION_TYPE_BIOMETRIC_MESSAGE,
- INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP
+ INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
+ INDICATION_IS_DISMISSIBLE
})
@Retention(RetentionPolicy.SOURCE)
public @interface IndicationType{}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index edf9648..e2ab20e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -593,6 +593,13 @@
mKeyguardViewMediator.doKeyguardTimeout(options);
}
+ // Binder interface
+ public void showDismissibleKeyguard() {
+ trace("showDismissibleKeyguard");
+ checkPermission();
+ mKeyguardViewMediator.showDismissibleKeyguard();
+ }
+
@Override // Binder interface
public void setSwitchingUser(boolean switching) {
trace("setSwitchingUser switching=" + switching);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index d7a1906..a34730e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -107,6 +107,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.app.animation.Interpolators;
+import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.logging.UiEventLogger;
@@ -311,6 +312,11 @@
*/
public static final String OPTION_FORCE_SHOW = "force_show";
public static final String SYS_BOOT_REASON_PROP = "sys.boot.reason.last";
+ /**
+ * Boolean option for showKeyguard, when set to true, can show the keyguard without immediately
+ * locking.
+ */
+ public static final String OPTION_SHOW_DISMISSIBLE = "show_dismissible";
public static final String REBOOT_MAINLINE_UPDATE = "reboot,mainline_update";
private final DreamOverlayStateController mDreamOverlayStateController;
private final JavaAdapter mJavaAdapter;
@@ -1321,7 +1327,9 @@
private DozeParameters mDozeParameters;
private SelectedUserInteractor mSelectedUserInteractor;
private KeyguardInteractor mKeyguardInteractor;
-
+ @VisibleForTesting
+ protected FoldGracePeriodProvider mFoldGracePeriodProvider =
+ new FoldGracePeriodProvider();
private final KeyguardStateController mKeyguardStateController;
private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
new KeyguardStateController.Callback() {
@@ -1995,7 +2003,7 @@
mNeedToReshowWhenReenabled = false;
updateInputRestrictedLocked();
- showLocked(null);
+ showKeyguard(null);
// block until we know the keyguard is done drawing (and post a message
// to unblock us after a timeout, so we don't risk blocking too long
@@ -2170,6 +2178,24 @@
}
/**
+ * Only available if the fold grace period feature is enabled.
+ * Used by PhoneWindowManager to show the keyguard immediately without locking the device.
+ * This method shows the keyguard whether there's a screen lock configured or not (including
+ * screen lock SWIPE or NONE).
+ * This must be safe to call from any thread and with any window manager locks held.
+ */
+ public void showDismissibleKeyguard() {
+ if (mFoldGracePeriodProvider.isEnabled()) {
+ Bundle showKeyguardUnlocked = new Bundle();
+ showKeyguardUnlocked.putBoolean(OPTION_SHOW_DISMISSIBLE, true);
+ showKeyguard(showKeyguardUnlocked);
+ } else {
+ Log.e(TAG, "fold grace period feature isn't enabled, but showKeyguard() method is"
+ + " being called", new Throwable());
+ }
+ }
+
+ /**
* Given the state of the keyguard, is the input restricted?
* Input is restricted when the keyguard is showing, or when the keyguard
* was suppressed by an app that disabled the keyguard or we haven't been provisioned yet.
@@ -2273,7 +2299,7 @@
}
if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
- showLocked(options);
+ showKeyguard(options);
}
private void lockProfile(int userId) {
@@ -2335,18 +2361,18 @@
}
/**
- * Send message to keyguard telling it to show itself
+ * Send message to keyguard telling it to show itself.
* @see #handleShow
*/
- private void showLocked(Bundle options) {
- Trace.beginSection("KeyguardViewMediator#showLocked acquiring mShowKeyguardWakeLock");
- if (DEBUG) Log.d(TAG, "showLocked");
+ private void showKeyguard(Bundle options) {
+ Trace.beginSection("KeyguardViewMediator#showKeyguard acquiring mShowKeyguardWakeLock");
+ if (DEBUG) Log.d(TAG, "showKeyguard");
// ensure we stay awake until we are finished displaying the keyguard
mShowKeyguardWakeLock.acquire();
Message msg = mHandler.obtainMessage(SHOW, options);
// Treat these messages with priority - This call can originate from #doKeyguardTimeout,
- // meaning the device should lock as soon as possible and not wait for other messages on
- // the thread to process first.
+ // meaning the device may lock, so it shouldn't wait for other messages on the thread to
+ // process first.
mHandler.sendMessageAtFrontOfQueue(msg);
Trace.endSection();
}
@@ -2724,13 +2750,18 @@
}
/**
- * Handle message sent by {@link #showLocked}.
+ * Handle message sent by {@link #showKeyguard}.
* @see #SHOW
*/
private void handleShow(Bundle options) {
Trace.beginSection("KeyguardViewMediator#handleShow");
+ final boolean showUnlocked = options != null
+ && options.getBoolean(OPTION_SHOW_DISMISSIBLE, false);
final int currentUser = mSelectedUserInteractor.getSelectedUserId();
- if (mLockPatternUtils.isSecure(currentUser)) {
+ if (showUnlocked) {
+ // tell KeyguardUpdateMonitor to keep the device unlocked until the next lock signal
+ mUpdateMonitor.tryForceIsDismissibleKeyguard();
+ } else if (mLockPatternUtils.isSecure(currentUser)) {
mLockPatternUtils.getDevicePolicyManager().reportKeyguardSecured(currentUser);
}
synchronized (KeyguardViewMediator.this) {
@@ -2755,7 +2786,7 @@
+ ", which means we're showing in the middle of hiding.");
}
- // Force if we we're showing in the middle of unlocking, to ensure we end up in the
+ // Force if we're showing in the middle of unlocking, to ensure we end up in the
// correct state.
setShowingLocked(true, hidingOrGoingAway /* force */);
mHiding = false;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt
index d8eb81c..f29b657 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt
@@ -16,10 +16,10 @@
package com.android.systemui.keyguard.dagger
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
-import com.android.systemui.keyguard.data.repository.NoopDeviceEntryFaceAuthRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
-import com.android.systemui.keyguard.domain.interactor.NoopKeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.data.repository.NoopDeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.NoopDeviceEntryFaceAuthInteractor
import dagger.Binds
import dagger.Module
@@ -33,7 +33,9 @@
@Module
interface KeyguardFaceAuthNotSupportedModule {
@Binds
- fun keyguardFaceAuthInteractor(impl: NoopKeyguardFaceAuthInteractor): KeyguardFaceAuthInteractor
+ fun keyguardFaceAuthInteractor(
+ impl: NoopDeviceEntryFaceAuthInteractor
+ ): DeviceEntryFaceAuthInteractor
@Binds
fun deviceEntryFaceAuthRepository(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
index ee0eb2d..b373f85 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
@@ -19,8 +19,10 @@
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
-import com.android.systemui.keyguard.domain.interactor.SystemUIKeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepositoryImpl
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.SystemUIDeviceEntryFaceAuthInteractor
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.TableLogBufferFactory
import dagger.Binds
@@ -38,13 +40,13 @@
@Binds
@IntoMap
- @ClassKey(SystemUIKeyguardFaceAuthInteractor::class)
- fun bind(impl: SystemUIKeyguardFaceAuthInteractor): CoreStartable
+ @ClassKey(SystemUIDeviceEntryFaceAuthInteractor::class)
+ fun bind(impl: SystemUIDeviceEntryFaceAuthInteractor): CoreStartable
@Binds
fun keyguardFaceAuthInteractor(
- impl: SystemUIKeyguardFaceAuthInteractor
- ): KeyguardFaceAuthInteractor
+ impl: SystemUIDeviceEntryFaceAuthInteractor
+ ): DeviceEntryFaceAuthInteractor
companion object {
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
deleted file mode 100644
index 046916a..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2023 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.keyguard.domain.interactor
-
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
-import kotlinx.coroutines.flow.Flow
-
-/**
- * Interactor that exposes API to get the face authentication status and handle any events that can
- * cause face authentication to run.
- */
-interface KeyguardFaceAuthInteractor {
-
- /** Current authentication status */
- val authenticationStatus: Flow<FaceAuthenticationStatus>
-
- /** Current detection status */
- val detectionStatus: Flow<FaceDetectionStatus>
-
- /** Can face auth be run right now */
- fun canFaceAuthRun(): Boolean
-
- /** Whether face auth is currently running or not. */
- fun isRunning(): Boolean
-
- /** Whether face auth is in lock out state. */
- fun isLockedOut(): Boolean
-
- /** Whether face auth is enrolled and enabled for the current user */
- fun isFaceAuthEnabledAndEnrolled(): Boolean
-
- /** Whether the current user is authenticated successfully with face auth */
- fun isAuthenticated(): Boolean
- /**
- * Register listener for use from code that cannot use [authenticationStatus] or
- * [detectionStatus]
- */
- fun registerListener(listener: FaceAuthenticationListener)
-
- /** Unregister previously registered listener */
- fun unregisterListener(listener: FaceAuthenticationListener)
-
- fun onUdfpsSensorTouched()
- fun onAssistantTriggeredOnLockScreen()
- fun onDeviceLifted()
- fun onQsExpansionStared()
- fun onNotificationPanelClicked()
- fun onSwipeUpOnBouncer()
- fun onPrimaryBouncerUserInput()
- fun onAccessibilityAction()
- fun onWalletLaunched()
-
- /** Whether face auth is considered class 3 */
- fun isFaceAuthStrong(): Boolean
-}
-
-/**
- * Listener that can be registered with the [KeyguardFaceAuthInteractor] to receive updates about
- * face authentication & detection updates.
- *
- * This is present to make it easier for use the new face auth API for code that cannot use
- * [KeyguardFaceAuthInteractor.authenticationStatus] or [KeyguardFaceAuthInteractor.detectionStatus]
- * flows.
- */
-interface FaceAuthenticationListener {
- /** Receive face isAuthenticated updates */
- fun onAuthenticatedChanged(isAuthenticated: Boolean)
-
- /** Receive face authentication status updates */
- fun onAuthenticationStatusChanged(status: FaceAuthenticationStatus)
-
- /** Receive status updates whenever face detection runs */
- fun onDetectionStatusChanged(status: FaceDetectionStatus)
-
- fun onLockoutStateChanged(isLockedOut: Boolean)
-
- fun onRunningStateChanged(isRunning: Boolean)
-
- fun onAuthEnrollmentStateChanged(enrolled: Boolean)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
deleted file mode 100644
index cd6ab31..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2023 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.keyguard.domain.interactor
-
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.emptyFlow
-
-/**
- * Implementation of the interactor that noops all face auth operations.
- *
- * This is required for SystemUI variants that do not support face authentication but still inject
- * other SysUI components that depend on [KeyguardFaceAuthInteractor]
- */
-@SysUISingleton
-class NoopKeyguardFaceAuthInteractor @Inject constructor() : KeyguardFaceAuthInteractor {
- override val authenticationStatus: Flow<FaceAuthenticationStatus>
- get() = emptyFlow()
- override val detectionStatus: Flow<FaceDetectionStatus>
- get() = emptyFlow()
-
- override fun canFaceAuthRun(): Boolean = false
-
- override fun isRunning(): Boolean = false
-
- override fun isLockedOut(): Boolean = false
-
- override fun isFaceAuthEnabledAndEnrolled(): Boolean = false
-
- override fun isFaceAuthStrong(): Boolean = false
-
- override fun isAuthenticated(): Boolean = false
-
- override fun registerListener(listener: FaceAuthenticationListener) {}
-
- override fun unregisterListener(listener: FaceAuthenticationListener) {}
-
- override fun onUdfpsSensorTouched() {}
-
- override fun onAssistantTriggeredOnLockScreen() {}
-
- override fun onDeviceLifted() {}
-
- override fun onQsExpansionStared() {}
-
- override fun onNotificationPanelClicked() {}
-
- override fun onSwipeUpOnBouncer() {}
- override fun onPrimaryBouncerUserInput() {}
- override fun onAccessibilityAction() {}
- override fun onWalletLaunched() = Unit
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt
index 942cd60..b3d0f918 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt
@@ -32,7 +32,7 @@
import android.os.PowerManager.WAKE_REASON_UNKNOWN
import android.util.Log
import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
/**
* Wrapper for [FaceAuthenticateOptions] to convert SystemUI values to their corresponding value in
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
index 5344696b..24d0602 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
@@ -30,11 +30,11 @@
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule
-import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeClockSection
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeMediaSection
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeNotificationStackScrollLayoutSection
+import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeSmartspaceSection
import com.android.systemui.util.kotlin.getOrNull
import java.util.Optional
import javax.inject.Inject
@@ -62,7 +62,7 @@
aodNotificationIconsSection: AodNotificationIconsSection,
aodBurnInSection: AodBurnInSection,
communalTutorialIndicatorSection: CommunalTutorialIndicatorSection,
- smartspaceSection: SmartspaceSection,
+ smartspaceSection: SplitShadeSmartspaceSection,
clockSection: SplitShadeClockSection,
mediaSection: SplitShadeMediaSection,
) : KeyguardBlueprint {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeSmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeSmartspaceSection.kt
new file mode 100644
index 0000000..8728ada
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeSmartspaceSection.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 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.keyguard.ui.view.layout.sections
+
+import android.content.Context
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
+import javax.inject.Inject
+
+/*
+ * We need this class for the splitShadeBlueprint so `addViews` and `removeViews` will be called
+ * when switching to and from splitShade.
+ */
+class SplitShadeSmartspaceSection
+@Inject
+constructor(
+ keyguardClockViewModel: KeyguardClockViewModel,
+ keyguardSmartspaceViewModel: KeyguardSmartspaceViewModel,
+ context: Context,
+ smartspaceController: LockscreenSmartspaceController,
+ keyguardUnlockAnimationController: KeyguardUnlockAnimationController,
+) :
+ SmartspaceSection(
+ keyguardClockViewModel,
+ keyguardSmartspaceViewModel,
+ context,
+ smartspaceController,
+ keyguardUnlockAnimationController,
+ )
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
index 3c2facb..9e6c552 100644
--- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -2,9 +2,9 @@
import android.hardware.face.FaceManager
import android.hardware.face.FaceSensorPropertiesInternal
-import com.android.keyguard.FaceAuthUiEvent
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.FaceAuthLog
import com.android.systemui.power.shared.model.WakeSleepReason
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 1e67771..24cb8ff 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -20,6 +20,7 @@
import com.android.systemui.common.data.repository.PackageChangeRepository;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepositoryImpl;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.LogBufferFactory;
import com.android.systemui.log.LogcatEchoTracker;
@@ -462,7 +463,7 @@
/**
* Provides a {@link LogBuffer} for use by
- * {@link com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepositoryImpl}.
+ * {@link DeviceEntryFaceAuthRepositoryImpl}.
*/
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 3e50dd3..ac0bd29 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -49,6 +49,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -231,32 +232,39 @@
if (!collapsedView && mQsTileRevealController != null) {
mQsTileRevealController.updateRevealedTiles(tiles);
}
- boolean shouldChange = false;
+ boolean shouldChangeAll = false;
+ // If the new tiles are a prefix of the old tiles, we delete the extra tiles (from the old).
+ // If not (even if they share a prefix) we remove all and add all the new ones.
if (tiles.size() <= mRecords.size()) {
int i = 0;
+ // Iterate through the requested tiles and check if they are the same as the existing
+ // tiles.
for (QSTile tile : tiles) {
if (tile != mRecords.get(i).tile) {
- shouldChange = true;
+ shouldChangeAll = true;
break;
}
i++;
}
- // If the first tiles are the same as the new ones, remove any extras.
- if (!shouldChange) {
- while (i < mRecords.size()) {
- QSPanelControllerBase.TileRecord record = mRecords.get(i);
+ // If the first tiles are the same as the new ones, we reuse them and remove any extra
+ // tiles.
+ if (!shouldChangeAll && i < mRecords.size()) {
+ List<TileRecord> extraRecords = mRecords.subList(i, mRecords.size());
+ for (QSPanelControllerBase.TileRecord record : extraRecords) {
mView.removeTile(record);
record.tile.removeCallback(record.callback);
- i++;
}
+ extraRecords.clear();
mCachedSpecs = getTilesSpecs();
}
} else {
- shouldChange = true;
+ shouldChangeAll = true;
}
- if (shouldChange) {
+ // If we detected that the existing tiles are different than the requested tiles, clear them
+ // and add the new tiles.
+ if (shouldChangeAll) {
for (QSPanelControllerBase.TileRecord record : mRecords) {
mView.removeTile(record);
record.tile.removeCallback(record.callback);
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 5abb4dd..c96651c 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -44,6 +44,7 @@
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
+import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
import com.android.systemui.util.asIndenting
import com.android.systemui.util.printSection
@@ -83,6 +84,7 @@
private val powerInteractor: PowerInteractor,
private val simBouncerInteractor: Lazy<SimBouncerInteractor>,
private val authenticationInteractor: Lazy<AuthenticationInteractor>,
+ private val windowController: NotificationShadeWindowController,
) : CoreStartable {
override fun start() {
@@ -92,6 +94,7 @@
automaticallySwitchScenes()
hydrateSystemUiState()
collectFalsingSignals()
+ hydrateWindowFocus()
} else {
sceneLogger.logFrameworkEnabled(
isEnabled = false,
@@ -348,6 +351,20 @@
}
}
+ /** Keeps the focus state of the window view up-to-date. */
+ private fun hydrateWindowFocus() {
+ applicationScope.launch {
+ sceneInteractor.transitionState
+ .mapNotNull { transitionState ->
+ (transitionState as? ObservableTransitionState.Idle)?.scene
+ }
+ .distinctUntilChanged()
+ .collect { sceneKey ->
+ windowController.setNotificationShadeFocusable(sceneKey != SceneKey.Gone)
+ }
+ }
+ }
+
private fun switchToScene(targetSceneKey: SceneKey, loggingReason: String) {
sceneInteractor.changeScene(
scene = SceneModel(targetSceneKey),
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index fb6bc38..a5c1cf1 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -26,6 +26,7 @@
import static com.android.keyguard.KeyguardClockSwitch.SMALL;
import static com.android.systemui.Flags.keyguardBottomAreaRefactor;
import static com.android.systemui.Flags.migrateClocksToBlueprint;
+import static com.android.systemui.Flags.predictiveBackAnimateShade;
import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
import static com.android.systemui.classifier.Classifier.GENERIC;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
@@ -121,6 +122,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.DumpsysTableLogger;
@@ -131,7 +133,6 @@
import com.android.systemui.keyguard.KeyguardViewConfigurator;
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver;
@@ -327,7 +328,7 @@
private final PulseExpansionHandler mPulseExpansionHandler;
private final KeyguardBypassController mKeyguardBypassController;
private final KeyguardUpdateMonitor mUpdateMonitor;
- private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+ private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
private final ConversationNotificationManager mConversationNotificationManager;
private final AuthController mAuthController;
private final MediaHierarchyManager mMediaHierarchyManager;
@@ -779,7 +780,7 @@
ActiveNotificationsInteractor activeNotificationsInteractor,
ShadeAnimationInteractor shadeAnimationInteractor,
KeyguardViewConfigurator keyguardViewConfigurator,
- KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
+ DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
SplitShadeStateController splitShadeStateController,
PowerInteractor powerInteractor,
KeyguardClockPositionAlgorithm keyguardClockPositionAlgorithm,
@@ -891,7 +892,7 @@
mShadeHeaderController = shadeHeaderController;
mLayoutInflater = layoutInflater;
mFeatureFlags = featureFlags;
- mAnimateBack = mFeatureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE);
+ mAnimateBack = predictiveBackAnimateShade();
mTrackpadGestureFeaturesEnabled = mFeatureFlags.isEnabled(Flags.TRACKPAD_GESTURE_FEATURES);
mFalsingCollector = falsingCollector;
mWakeUpCoordinator = coordinator;
@@ -936,7 +937,7 @@
mScreenOffAnimationController = screenOffAnimationController;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
mLastDownEvents = new NPVCDownEventState.Buffer(MAX_DOWN_EVENT_BUFFER_SIZE);
- mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+ mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
int currentMode = navigationModeController.addListener(
mode -> mIsGestureNavigation = QuickStepContract.isGesturalMode(mode));
@@ -2978,9 +2979,9 @@
mShadeLog.v("onMiddleClicked on Keyguard, mDozingOnDown: false");
// Try triggering face auth, this "might" run. Check
// KeyguardUpdateMonitor#shouldListenForFace to see when face auth won't run.
- mKeyguardFaceAuthInteractor.onNotificationPanelClicked();
+ mDeviceEntryFaceAuthInteractor.onNotificationPanelClicked();
- if (mKeyguardFaceAuthInteractor.canFaceAuthRun()) {
+ if (mDeviceEntryFaceAuthInteractor.canFaceAuthRun()) {
mUpdateMonitor.requestActiveUnlock(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
"lockScreenEmptySpaceTap");
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index 8397caa..1dff99d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -66,9 +66,9 @@
import com.android.systemui.Dumpable;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
import com.android.systemui.media.controls.pipeline.MediaDataManager;
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
@@ -152,7 +152,7 @@
private final RecordingController mRecordingController;
private final LockscreenGestureLogger mLockscreenGestureLogger;
private final ShadeLogger mShadeLog;
- private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+ private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
private final CastController mCastController;
private final SplitShadeStateController mSplitShadeStateController;
private final InteractionJankMonitor mInteractionJankMonitor;
@@ -338,7 +338,7 @@
InteractionJankMonitor interactionJankMonitor,
ShadeLogger shadeLog,
DumpManager dumpManager,
- KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
+ DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
ShadeRepository shadeRepository,
ShadeInteractor shadeInteractor,
ActiveNotificationsInteractor activeNotificationsInteractor,
@@ -384,7 +384,7 @@
mLockscreenGestureLogger = lockscreenGestureLogger;
mMetricsLogger = metricsLogger;
mShadeLog = shadeLog;
- mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+ mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
mCastController = castController;
mInteractionJankMonitor = interactionJankMonitor;
mShadeRepository = shadeRepository;
@@ -972,7 +972,7 @@
// When expanding QS, let's authenticate the user if possible,
// this will speed up notification actions.
if (height == 0 && !mKeyguardStateController.canDismissLockScreen()) {
- mKeyguardFaceAuthInteractor.onQsExpansionStared();
+ mDeviceEntryFaceAuthInteractor.onQsExpansionStared();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 08415cb..d6d3e67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -31,6 +31,7 @@
import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.IMPORTANT_MSG_MIN_DURATION;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_IS_DISMISSIBLE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE;
@@ -178,6 +179,7 @@
private KeyguardInteractor mKeyguardInteractor;
private String mPersistentUnlockMessage;
private String mAlignmentIndication;
+ private boolean mForceIsDismissible;
private CharSequence mTrustGrantedIndication;
private CharSequence mTransientIndication;
private CharSequence mBiometricMessage;
@@ -442,6 +444,7 @@
// Update persistent messages. The following methods should only be called if we're on the
// lock screen:
+ updateForceIsDimissibileChanged();
updateLockScreenDisclosureMsg();
updateLockScreenOwnerInfo();
updateLockScreenBatteryMsg(animate);
@@ -458,6 +461,22 @@
updateDeviceEntryIndication(false);
}
+ private void updateForceIsDimissibileChanged() {
+ if (mForceIsDismissible) {
+ mRotateTextViewController.updateIndication(
+ INDICATION_IS_DISMISSIBLE,
+ new KeyguardIndication.Builder()
+ .setMessage(mContext.getResources().getString(
+ com.android.systemui.res.R.string.dismissible_keyguard_swipe)
+ )
+ .setTextColor(mInitialTextColorState)
+ .build(),
+ /* updateImmediately */ true);
+ } else {
+ mRotateTextViewController.hideIndication(INDICATION_IS_DISMISSIBLE);
+ }
+ }
+
private void updateLockScreenDisclosureMsg() {
if (mOrganizationOwnedDevice) {
mBackgroundExecutor.execute(() -> {
@@ -1311,6 +1330,12 @@
}
@Override
+ public void onForceIsDismissibleChanged(boolean forceIsDismissible) {
+ mForceIsDismissible = forceIsDismissible;
+ updateDeviceEntryIndication(false);
+ }
+
+ @Override
public void onTrustGrantedForCurrentUser(
boolean dismissKeyguard,
boolean newlyUnlocked,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
index 4b89615..9fdd0bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
@@ -18,7 +18,7 @@
import android.os.PowerManager
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.statusbar.LockscreenShadeTransitionController
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 6f5058c..2e54512 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
@@ -366,8 +366,7 @@
@Override
public void onStatePostChange() {
- mView.updateSensitiveness(mStatusBarStateController.goingToFullShade(),
- mLockscreenUserManager.isAnyProfilePublicMode());
+ updateSensitivenessWithAnimation(mStatusBarStateController.goingToFullShade());
mView.onStatePostChange(mStatusBarStateController.fromShadeLocked());
if (!FooterViewRefactor.isEnabled()) {
updateImportantForAccessibility();
@@ -378,7 +377,7 @@
private final UserChangedListener mLockscreenUserChangeListener = new UserChangedListener() {
@Override
public void onUserChanged(int userId) {
- mView.updateSensitiveness(false, mLockscreenUserManager.isAnyProfilePublicMode());
+ updateSensitivenessWithAnimation(false);
mHistoryEnabled = null;
updateFooter();
}
@@ -388,7 +387,11 @@
* Recalculate sensitiveness without animation; called when waking up while keyguard occluded.
*/
public void updateSensitivenessForOccludedWakeup() {
- mView.updateSensitiveness(false, mLockscreenUserManager.isAnyProfilePublicMode());
+ updateSensitivenessWithAnimation(false);
+ }
+
+ private void updateSensitivenessWithAnimation(boolean animate) {
+ mView.updateSensitiveness(animate, mLockscreenUserManager.isAnyProfilePublicMode());
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 57d49b2..6e3aabf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -31,6 +31,7 @@
import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
+import static com.android.systemui.Flags.predictiveBackSysui;
import android.annotation.Nullable;
import android.app.ActivityOptions;
@@ -836,7 +837,7 @@
mLightRevealScrim = lightRevealScrim;
// Based on teamfood flag, turn predictive back dispatch on at runtime.
- if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI)) {
+ if (predictiveBackSysui()) {
mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index 32b3ac2..9f08633 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -28,7 +28,7 @@
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.Assert
@@ -43,13 +43,13 @@
*/
@SysUISingleton
class KeyguardLiftController @Inject constructor(
- private val context: Context,
- private val statusBarStateController: StatusBarStateController,
- private val asyncSensorManager: AsyncSensorManager,
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor,
- private val dumpManager: DumpManager,
- private val selectedUserInteractor: SelectedUserInteractor,
+ private val context: Context,
+ private val statusBarStateController: StatusBarStateController,
+ private val asyncSensorManager: AsyncSensorManager,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
+ private val dumpManager: DumpManager,
+ private val selectedUserInteractor: SelectedUserInteractor,
) : Dumpable, CoreStartable {
private val pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE)
@@ -75,7 +75,7 @@
// Not listening anymore since trigger events unregister themselves
isListening = false
updateListeningState()
- keyguardFaceAuthInteractor.onDeviceLifted()
+ deviceEntryFaceAuthInteractor.onDeviceLifted()
keyguardUpdateMonitor.requestActiveUnlock(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE,
"KeyguardLiftController")
@@ -113,7 +113,7 @@
val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible &&
!statusBarStateController.isDozing
- val isFaceEnabled = keyguardFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()
+ val isFaceEnabled = deviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()
val shouldListen = (onKeyguard || bouncerVisible) && isFaceEnabled
if (shouldListen != isListening) {
isListening = shouldListen
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 4999123..88347ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -18,6 +18,7 @@
import static android.view.WindowInsets.Type.navigationBars;
+import static com.android.systemui.Flags.predictiveBackAnimateBouncer;
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
@@ -400,8 +401,7 @@
mFoldAodAnimationController = sysUIUnfoldComponent
.map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
mAlternateBouncerInteractor = alternateBouncerInteractor;
- mIsBackAnimationEnabled =
- featureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM);
+ mIsBackAnimationEnabled = predictiveBackAnimateBouncer();
mUdfpsOverlayInteractor = udfpsOverlayInteractor;
mActivityStarter = activityStarter;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 16334d3..886010c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -493,5 +493,10 @@
public void onEnabledTrustAgentsChanged(int userId) {
update(false /* updateAlways */);
}
+
+ @Override
+ public void onForceIsDismissibleChanged(boolean keepUnlockedOnFold) {
+ update(false /* updateAlways */);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index 5558aa7..111492c 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -43,7 +43,7 @@
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.res.R;
@@ -69,7 +69,7 @@
private final Executor mExecutor;
private final Handler mHandler;
private final FalsingManager mFalsingManager;
- private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+ private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
private FalsingCollector mFalsingCollector;
private final UserTracker mUserTracker;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -94,7 +94,7 @@
KeyguardUpdateMonitor keyguardUpdateMonitor,
StatusBarKeyguardViewManager keyguardViewManager,
UiEventLogger uiEventLogger,
- KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) {
+ DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor) {
mKeyguardStateController = keyguardStateController;
mKeyguardDismissUtil = keyguardDismissUtil;
mActivityStarter = activityStarter;
@@ -106,7 +106,7 @@
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mKeyguardViewManager = keyguardViewManager;
mUiEventLogger = uiEventLogger;
- mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+ mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
}
@Override
@@ -213,7 +213,7 @@
true,
Utils.getColorAttrDefaultColor(
this, com.android.internal.R.attr.colorAccentPrimary));
- mKeyguardFaceAuthInteractor.onWalletLaunched();
+ mDeviceEntryFaceAuthInteractor.onWalletLaunched();
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index c5ce856..d8eb05a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -110,6 +110,7 @@
import android.text.TextUtils;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
@@ -120,16 +121,19 @@
import com.android.keyguard.KeyguardUpdateMonitor.BiometricAuthenticated;
import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
import com.android.settingslib.fuelgauge.BatteryStatus;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig;
+import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigImpl;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
+import com.android.systemui.deviceentry.domain.interactor.FaceAuthenticationListener;
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus;
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus;
+import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus;
-import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.UserTracker;
@@ -219,6 +223,8 @@
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
+ private FoldGracePeriodProvider mFoldGracePeriodProvider;
+ @Mock
private TelephonyManager mTelephonyManager;
@Mock
private SensorPrivacyManager mSensorPrivacyManager;
@@ -261,7 +267,7 @@
@Mock
private SelectedUserInteractor mSelectedUserInteractor;
@Mock
- private KeyguardFaceAuthInteractor mFaceAuthInteractor;
+ private DeviceEntryFaceAuthInteractor mFaceAuthInteractor;
@Captor
private ArgumentCaptor<FaceAuthenticationListener> mFaceAuthenticationListener;
@@ -315,7 +321,7 @@
mContext.getOrCreateTestableResources().addOverride(
com.android.systemui.res.R.integer.config_face_auth_supported_posture,
DEVICE_POSTURE_UNKNOWN);
- mFaceWakeUpTriggersConfig = new FaceWakeUpTriggersConfig(
+ mFaceWakeUpTriggersConfig = new FaceWakeUpTriggersConfigImpl(
mContext.getResources(),
mGlobalSettings,
mDumpManager
@@ -335,6 +341,7 @@
anyInt());
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
+ mKeyguardUpdateMonitor.mFoldGracePeriodProvider = mFoldGracePeriodProvider;
setupBiometrics(mKeyguardUpdateMonitor);
mKeyguardUpdateMonitor.setFaceAuthInteractor(mFaceAuthInteractor);
verify(mFaceAuthInteractor).registerListener(mFaceAuthenticationListener.capture());
@@ -923,8 +930,7 @@
@Test
public void trustAgentHasTrust() {
// WHEN user has trust
- mKeyguardUpdateMonitor.onTrustChanged(true, true,
- mSelectedUserInteractor.getSelectedUserId(), 0, null);
+ givenSelectedUserCanSkipBouncerFromTrustedState();
// THEN user is considered as "having trust" and bouncer can be skipped
Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(
@@ -948,8 +954,7 @@
@Test
public void trustAgentHasTrust_fingerprintLockout() {
// GIVEN user has trust
- mKeyguardUpdateMonitor.onTrustChanged(true, true,
- mSelectedUserInteractor.getSelectedUserId(), 0, null);
+ givenSelectedUserCanSkipBouncerFromTrustedState();
Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(
mSelectedUserInteractor.getSelectedUserId()));
@@ -1992,6 +1997,43 @@
}
@Test
+ public void runFpDetectFlagDisabled_sideFps_keyguardDismissible_fingerprintAuthenticateRuns() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_RUN_FINGERPRINT_DETECT_ON_DISMISSIBLE_KEYGUARD);
+
+ // Clear invocations, since previous setup (e.g. registering BiometricManager callbacks)
+ // will trigger updateBiometricListeningState();
+ clearInvocations(mFingerprintManager);
+ mKeyguardUpdateMonitor.resetBiometricListeningState();
+
+ // GIVEN the user can skip the bouncer
+ givenSelectedUserCanSkipBouncerFromTrustedState();
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+ mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
+ mTestableLooper.processAllMessages();
+
+ // WHEN verify authenticate runs
+ verifyFingerprintAuthenticateCall();
+ }
+
+ @Test
+ public void sideFps_keyguardDismissible_fingerprintDetectRuns() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RUN_FINGERPRINT_DETECT_ON_DISMISSIBLE_KEYGUARD);
+ // Clear invocations, since previous setup (e.g. registering BiometricManager callbacks)
+ // will trigger updateBiometricListeningState();
+ clearInvocations(mFingerprintManager);
+ mKeyguardUpdateMonitor.resetBiometricListeningState();
+
+ // GIVEN the user can skip the bouncer
+ givenSelectedUserCanSkipBouncerFromTrustedState();
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+ mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
+ mTestableLooper.processAllMessages();
+
+ // WHEN verify detect runs
+ verifyFingerprintDetectCall();
+ }
+
+ @Test
public void testFingerprintSensorProperties() throws RemoteException {
mFingerprintAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(
new ArrayList<>());
@@ -2096,6 +2138,35 @@
verify(callback).onBiometricEnrollmentStateChanged(BiometricSourceType.FACE);
}
+ private void givenSelectedUserCanSkipBouncerFromTrustedState() {
+ mKeyguardUpdateMonitor.onTrustChanged(true, true,
+ mSelectedUserInteractor.getSelectedUserId(), 0, null);
+ }
+
+ @Test
+ public void forceIsDismissibleKeyguard_foldingGracePeriodNotEnabled() {
+ when(mFoldGracePeriodProvider.isEnabled()).thenReturn(false);
+ primaryAuthNotRequiredByStrongAuthTracker();
+ mKeyguardUpdateMonitor.tryForceIsDismissibleKeyguard();
+ Assert.assertFalse(mKeyguardUpdateMonitor.forceIsDismissibleIsKeepingDeviceUnlocked());
+ }
+
+ @Test
+ public void forceIsDismissibleKeyguard() {
+ when(mFoldGracePeriodProvider.isEnabled()).thenReturn(true);
+ primaryAuthNotRequiredByStrongAuthTracker();
+ mKeyguardUpdateMonitor.tryForceIsDismissibleKeyguard();
+ Assert.assertTrue(mKeyguardUpdateMonitor.forceIsDismissibleIsKeepingDeviceUnlocked());
+ }
+
+ @Test
+ public void forceIsDismissibleKeyguard_respectsLockdown() {
+ when(mFoldGracePeriodProvider.isEnabled()).thenReturn(true);
+ userDeviceLockDown();
+ mKeyguardUpdateMonitor.tryForceIsDismissibleKeyguard();
+ Assert.assertFalse(mKeyguardUpdateMonitor.forceIsDismissibleIsKeepingDeviceUnlocked());
+ }
+
private void verifyFingerprintAuthenticateNeverCalled() {
verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), any());
verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
index 8693d5c..7c626a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
@@ -16,6 +16,9 @@
package com.android.systemui.back.domain.interactor
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.view.ViewRootImpl
import android.window.BackEvent
import android.window.BackEvent.EDGE_LEFT
@@ -26,9 +29,8 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.statusbar.IStatusBarService
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
@@ -72,7 +74,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
class BackActionInteractorTest : SysuiTestCase() {
private val testScope = TestScope()
- private val featureFlags = FakeFeatureFlags()
private val executor = FakeExecutor(FakeSystemClock())
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
@@ -107,17 +108,17 @@
statusBarKeyguardViewManager,
shadeController,
notificationShadeWindowController,
- windowRootViewVisibilityInteractor,
- featureFlags,
+ windowRootViewVisibilityInteractor
)
.apply { this.setup(qsController, shadeViewController) }
}
private val powerInteractor = PowerInteractorFactory.create().powerInteractor
+ @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
@Before
fun setUp() {
- featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false)
whenever(notificationShadeWindowController.windowRootView).thenReturn(windowRootView)
whenever(windowRootView.viewRootImpl).thenReturn(viewRootImpl)
whenever(viewRootImpl.onBackInvokedDispatcher).thenReturn(onBackInvokedDispatcher)
@@ -229,9 +230,9 @@
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
fun animationFlagOff_onBackInvoked_keyguardNotified() {
backActionInteractor.start()
- featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false)
windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
powerInteractor.setAwakeForTest()
val callback = getBackInvokedCallback()
@@ -243,8 +244,8 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
fun animationFlagOn_onBackInvoked_keyguardNotified() {
- featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
backActionInteractor.start()
windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
powerInteractor.setAwakeForTest()
@@ -257,8 +258,8 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
fun animationFlagOn_callbackIsAnimationCallback() {
- featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
backActionInteractor.start()
windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
powerInteractor.setAwakeForTest()
@@ -269,8 +270,8 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
fun onBackProgressed_shadeCannotBeCollapsed_shadeViewControllerNotNotified() {
- featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
backActionInteractor.start()
windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
powerInteractor.setAwakeForTest()
@@ -284,8 +285,8 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
fun onBackProgressed_shadeCanBeCollapsed_shadeViewControllerNotified() {
- featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
backActionInteractor.start()
windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
powerInteractor.setAwakeForTest()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 1f7dd6d..c143bc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -123,7 +123,6 @@
udfpsControllerProvider,
statusBarStateController,
displayMetrics,
- featureFlags,
KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)),
biometricUnlockController,
lightRevealScrim,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt
index 86b9b84..9b0e58d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt
@@ -22,7 +22,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.whenever
@@ -42,7 +42,7 @@
class FaceAuthAccessibilityDelegateTest : SysuiTestCase() {
@Mock private lateinit var hostView: View
- @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+ @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
private lateinit var underTest: FaceAuthAccessibilityDelegate
@Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
index 755fa02..54dbd04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
@@ -57,13 +57,13 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.display.data.repository.FakeDisplayRepository
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
import com.android.systemui.log.SideFpsLogger
import com.android.systemui.log.logcatLogBuffer
@@ -110,7 +110,7 @@
@JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
@Mock private lateinit var activityTaskManager: ActivityTaskManager
@Mock private lateinit var displayManager: DisplayManager
- @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+ @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
@Mock
private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider
@Mock private lateinit var fpsUnlockTracker: FpsUnlockTracker
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
index bdca948..1fa60fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
@@ -55,13 +55,13 @@
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.display.data.repository.FakeDisplayRepository
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.keyguard.domain.interactor.DeviceEntrySideFpsOverlayInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
import com.android.systemui.log.SideFpsLogger
import com.android.systemui.log.logcatLogBuffer
@@ -101,7 +101,7 @@
@JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
@Mock private lateinit var activityTaskManager: ActivityTaskManager
- @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+ @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
@Mock
private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
index 45a426e..e796303 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
@@ -36,13 +36,13 @@
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.flags.SystemPropertiesHelper
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.keyguard.shared.model.AuthenticationFlags
import com.android.systemui.res.R.string.kg_too_many_failed_attempts_countdown
import com.android.systemui.res.R.string.kg_trust_agent_disabled
@@ -122,7 +122,7 @@
fakeTrustRepository,
testScope.backgroundScope,
mSelectedUserInteractor,
- mock(KeyguardFaceAuthInteractor::class.java),
+ mock(DeviceEntryFaceAuthInteractor::class.java),
)
underTest =
BouncerMessageInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
index dacf23a..ee46f76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
@@ -32,9 +32,9 @@
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.bouncer.ui.BouncerViewDelegate
import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@@ -73,7 +73,7 @@
@Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
- @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+ @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
private lateinit var mainHandler: FakeHandler
private lateinit var underTest: PrimaryBouncerInteractor
private lateinit var resources: TestableResources
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
index 59bcf01..d5c3641 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryHapticsInteractorTest.kt
@@ -24,13 +24,13 @@
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor
+import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.data.repository.powerRepository
import com.android.systemui.power.shared.model.WakeSleepReason
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/FaceWakeUpTriggersConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt
similarity index 91%
rename from packages/SystemUI/tests/src/com/android/keyguard/FaceWakeUpTriggersConfigTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt
index 94c34a5..e9b4bbb 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/FaceWakeUpTriggersConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.keyguard
+package com.android.systemui.deviceentry.data.repository
import android.os.PowerManager
import android.testing.AndroidTestingRunner
@@ -71,6 +71,6 @@
wakeUpTriggers
)
- return FaceWakeUpTriggersConfig(mContext.getResources(), globalSettings, dumpManager)
+ return FaceWakeUpTriggersConfigImpl(mContext.resources, globalSettings, dumpManager)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
index 769cf45..368d1d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
@@ -1,21 +1,20 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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
+ * 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
+ * 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.
- *
+ * 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.keyguard.domain.interactor
+package com.android.systemui.deviceentry.domain.interactor
import android.app.trust.TrustManager
import android.content.pm.UserInfo
@@ -25,8 +24,6 @@
import android.os.PowerManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.keyguard.FaceAuthUiEvent
-import com.android.keyguard.FaceWakeUpTriggersConfig
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
@@ -42,6 +39,9 @@
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
@@ -49,7 +49,8 @@
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -87,9 +88,9 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
-class KeyguardFaceAuthInteractorTest : SysuiTestCase() {
+class DeviceEntryFaceAuthInteractorTest : SysuiTestCase() {
- private lateinit var underTest: SystemUIKeyguardFaceAuthInteractor
+ private lateinit var underTest: SystemUIDeviceEntryFaceAuthInteractor
private lateinit var testScope: TestScope
private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
@@ -133,7 +134,7 @@
fakeBiometricSettingsRepository = FakeBiometricSettingsRepository()
underTest =
- SystemUIKeyguardFaceAuthInteractor(
+ SystemUIDeviceEntryFaceAuthInteractor(
mContext,
testScope.backgroundScope,
dispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/FaceAuthReasonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/shared/FaceAuthReasonTest.kt
similarity index 92%
rename from packages/SystemUI/tests/src/com/android/keyguard/FaceAuthReasonTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/deviceentry/shared/FaceAuthReasonTest.kt
index 68d0f41..ef89752 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/FaceAuthReasonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/shared/FaceAuthReasonTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.keyguard
+
+package com.android.systemui.deviceentry.shared
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index b38c9ec..b57cf53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -72,6 +72,7 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
@@ -324,6 +325,54 @@
@Test
@TestableLooper.RunWithLooper(setAsMainLooper = true)
+ public void showKeyguardAfterKeyguardNotEnabled() {
+ // GIVEN feature is enabled
+ final FoldGracePeriodProvider mockedFoldGracePeriodProvider =
+ mock(FoldGracePeriodProvider.class);
+ mViewMediator.mFoldGracePeriodProvider = mockedFoldGracePeriodProvider;
+ when(mockedFoldGracePeriodProvider.isEnabled()).thenReturn(true);
+
+ // GIVEN keyguard is not enabled and isn't showing
+ mViewMediator.onSystemReady();
+ mViewMediator.setKeyguardEnabled(false);
+ TestableLooper.get(this).processAllMessages();
+ captureKeyguardUpdateMonitorCallback();
+ assertFalse(mViewMediator.isShowingAndNotOccluded());
+
+ // WHEN showKeyguard is requested
+ mViewMediator.showDismissibleKeyguard();
+
+ // THEN keyguard is shown
+ TestableLooper.get(this).processAllMessages();
+ assertTrue(mViewMediator.isShowingAndNotOccluded());
+ }
+
+ @Test
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
+ public void showKeyguardAfterKeyguardNotEnabled_featureNotEnabled() {
+ // GIVEN feature is NOT enabled
+ final FoldGracePeriodProvider mockedFoldGracePeriodProvider =
+ mock(FoldGracePeriodProvider.class);
+ mViewMediator.mFoldGracePeriodProvider = mockedFoldGracePeriodProvider;
+ when(mockedFoldGracePeriodProvider.isEnabled()).thenReturn(false);
+
+ // GIVEN keyguard is not enabled and isn't showing
+ mViewMediator.onSystemReady();
+ mViewMediator.setKeyguardEnabled(false);
+ TestableLooper.get(this).processAllMessages();
+ captureKeyguardUpdateMonitorCallback();
+ assertFalse(mViewMediator.isShowingAndNotOccluded());
+
+ // WHEN showKeyguard is requested
+ mViewMediator.showDismissibleKeyguard();
+
+ // THEN keyguard is still NOT shown
+ TestableLooper.get(this).processAllMessages();
+ assertFalse(mViewMediator.isShowingAndNotOccluded());
+ }
+
+ @Test
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
public void doNotHideKeyguard_whenLockdown_onKeyguardNotEnabledExternally() {
// GIVEN keyguard is enabled and lockdown occurred so the keyguard is showing
mViewMediator.onSystemReady();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
index 027dfa1..c4df27c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
@@ -29,6 +29,7 @@
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
@@ -62,7 +63,7 @@
class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
@JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
- @Mock private lateinit var faceAuthInteractor: KeyguardFaceAuthInteractor
+ @Mock private lateinit var faceAuthInteractor: DeviceEntryFaceAuthInteractor
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
index 1f245f1..7358b9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
@@ -147,7 +147,7 @@
trustRepository,
testScope.backgroundScope,
mSelectedUserInteractor,
- keyguardFaceAuthInteractor = mock(),
+ deviceEntryFaceAuthInteractor = mock(),
),
AlternateBouncerInteractor(
statusBarStateController = mock(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 018fa9e..a92111e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -394,6 +394,7 @@
public void setTiles_differentTiles_extraTileRemoved() {
when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
mController.setTiles();
+ assertEquals(2, mController.mRecords.size());
clearInvocations(mQSPanel);
@@ -402,6 +403,7 @@
verify(mQSPanel, times(1)).removeTile(any());
verify(mQSPanel, never()).addTile(any());
+ assertEquals(1, mController.mRecords.size());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 2220756..3132767 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -86,6 +86,7 @@
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
import com.android.systemui.common.ui.view.LongPressHandlingView;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.dump.DumpManager;
@@ -98,7 +99,6 @@
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
@@ -337,7 +337,7 @@
protected ArgumentCaptor<NotificationStackScrollLayout.OnEmptySpaceClickListener>
mEmptySpaceClickListenerCaptor;
@Mock protected ActivityStarter mActivityStarter;
- @Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+ @Mock protected DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
@Mock private JavaAdapter mJavaAdapter;
@Mock private CastController mCastController;
@Mock private SharedNotificationContainerInteractor mSharedNotificationContainerInteractor;
@@ -382,7 +382,6 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false);
mFeatureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false);
mFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false);
mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false);
@@ -730,7 +729,7 @@
mActiveNotificationsInteractor,
mShadeAnimationInteractor,
mKeyguardViewConfigurator,
- mKeyguardFaceAuthInteractor,
+ mDeviceEntryFaceAuthInteractor,
new ResourcesSplitShadeStateController(),
mPowerInteractor,
mKeyguardClockPositionAlgorithm,
@@ -800,7 +799,7 @@
mInteractionJankMonitor,
mShadeLog,
mDumpManager,
- mKeyguardFaceAuthInteractor,
+ mDeviceEntryFaceAuthInteractor,
mShadeRepository,
mShadeInteractor,
mActiveNotificationsInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 3cbb9bb..2e8d46a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -1120,7 +1120,7 @@
mTouchHandler.onTouch(mock(View.class), mDownMotionEvent);
mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0);
- verify(mKeyguardFaceAuthInteractor).onNotificationPanelClicked();
+ verify(mDeviceEntryFaceAuthInteractor).onNotificationPanelClicked();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index c21addc..ee7c6c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -62,7 +62,7 @@
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -257,7 +257,7 @@
FakeTrustRepository(),
testScope.backgroundScope,
mSelectedUserInteractor,
- mock(KeyguardFaceAuthInteractor::class.java)
+ mock(DeviceEntryFaceAuthInteractor::class.java)
),
facePropertyRepository = FakeFacePropertyRepository(),
deviceEntryFingerprintAuthRepository =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index eb5633b..727a6c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -42,6 +42,7 @@
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository;
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlagsClassic;
@@ -55,7 +56,6 @@
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.media.controls.pipeline.MediaDataManager;
@@ -378,7 +378,7 @@
mInteractionJankMonitor,
mShadeLogger,
mDumpManager,
- mock(KeyguardFaceAuthInteractor.class),
+ mock(DeviceEntryFaceAuthInteractor.class),
mShadeRepository,
mShadeInteractor,
mActiveNotificationsInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index e339636..316f2b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -334,15 +334,8 @@
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
- // CentralSurfacesImpl's runtime flag check fails if the flag is absent.
- // This value is unused, because test manifest is opted in.
- mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI, false);
// Set default value to avoid IllegalStateException.
mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false);
- // For the Shade to respond to Back gesture, we must enable the event routing
- mFeatureFlags.set(Flags.WM_SHADE_ALLOW_BACK_GESTURE, true);
- // For the Shade to animate during the Back gesture, we must enable the animation flag.
- mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true);
mSetFlagsRule.enableFlags(FLAG_LIGHT_REVEAL_MIGRATION);
// Turn AOD on and toggle feature flag for jank fixes
mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 225ddb6..8dde935 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -37,6 +37,9 @@
import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.service.trust.TrustAgentService;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -98,6 +101,7 @@
import com.google.common.truth.Truth;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -165,6 +169,8 @@
@Captor
private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallback;
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@Before
public void setUp() {
@@ -175,7 +181,6 @@
when(mBouncerView.getDelegate()).thenReturn(mBouncerViewDelegate);
when(mBouncerViewDelegate.getBackCallback()).thenReturn(mBouncerViewDelegateBackCallback);
mFeatureFlags = new FakeFeatureFlags();
- mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM, true);
mFeatureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false);
mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false);
mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
@@ -584,6 +589,7 @@
}
@Test
+ @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
public void testPredictiveBackCallback_registration() {
/* verify that a predictive back callback is registered when the bouncer becomes visible */
mBouncerExpansionCallback.onVisibilityChanged(true);
@@ -598,6 +604,7 @@
}
@Test
+ @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
public void testPredictiveBackCallback_invocationHidesBouncer() {
mBouncerExpansionCallback.onVisibilityChanged(true);
/* capture the predictive back callback during registration */
@@ -615,6 +622,7 @@
}
@Test
+ @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
public void testPredictiveBackCallback_noBackAnimationForFullScreenBouncer() {
when(mKeyguardSecurityModel.getSecurityMode(anyInt()))
.thenReturn(KeyguardSecurityModel.SecurityMode.SimPin);
@@ -634,6 +642,7 @@
}
@Test
+ @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
public void testPredictiveBackCallback_forwardsBackDispatches() {
mBouncerExpansionCallback.onVisibilityChanged(true);
/* capture the predictive back callback during registration */
diff --git a/packages/SystemUI/tests/utils/src/com/android/keyguard/TrustManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/keyguard/TrustManagerKosmos.kt
new file mode 100644
index 0000000..187935b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/keyguard/TrustManagerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 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.app.trust.TrustManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.trustManager by Kosmos.Fixture { mock<TrustManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryKosmos.kt
new file mode 100644
index 0000000..6ef7419
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 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.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.facePropertyRepository by Fixture { FakeFacePropertyRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigKosmos.kt
new file mode 100644
index 0000000..21cff0d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 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.deviceentry.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig by
+ Kosmos.Fixture { FakeFaceWakeUpTriggersConfig() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeFaceWakeUpTriggersConfig.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeFaceWakeUpTriggersConfig.kt
new file mode 100644
index 0000000..af617b7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeFaceWakeUpTriggersConfig.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 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.deviceentry.data.repository
+
+import com.android.systemui.power.shared.model.WakeSleepReason
+
+class FakeFaceWakeUpTriggersConfig : FaceWakeUpTriggersConfig {
+ private val triggerFaceAuthOnWakeUpFrom: MutableSet<Int> = mutableSetOf()
+ private val wakeSleepReasonsToTriggerFaceAuth: MutableSet<WakeSleepReason> = mutableSetOf()
+ override fun shouldTriggerFaceAuthOnWakeUpFrom(pmWakeReason: Int): Boolean {
+ return triggerFaceAuthOnWakeUpFrom.contains(pmWakeReason)
+ }
+
+ override fun shouldTriggerFaceAuthOnWakeUpFrom(wakeReason: WakeSleepReason): Boolean {
+ return wakeSleepReasonsToTriggerFaceAuth.contains(wakeReason)
+ }
+
+ fun setTriggerFaceAuthOnWakeUpFrom(pmWakeReasons: Set<Int>) {
+ triggerFaceAuthOnWakeUpFrom.clear()
+ triggerFaceAuthOnWakeUpFrom.addAll(pmWakeReasons)
+
+ wakeSleepReasonsToTriggerFaceAuth.clear()
+ wakeSleepReasonsToTriggerFaceAuth.addAll(
+ pmWakeReasons.map { WakeSleepReason.fromPowerManagerWakeReason(it) }
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt
index d2dff78..0b1fb40 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt
@@ -18,13 +18,45 @@
package com.android.systemui.deviceentry.domain.interactor
+import android.content.applicationContext
+import com.android.keyguard.keyguardUpdateMonitor
+import com.android.keyguard.trustManager
+import com.android.systemui.biometrics.data.repository.facePropertyRepository
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
+import com.android.systemui.deviceentry.data.repository.faceWakeUpTriggersConfig
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.deviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.user.data.repository.userRepository
+import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.ExperimentalCoroutinesApi
+val Kosmos.faceAuthLogger by Kosmos.Fixture { mock<FaceAuthenticationLogger>() }
val Kosmos.deviceEntryFaceAuthInteractor by
Kosmos.Fixture {
- DeviceEntryFaceAuthInteractor(
+ SystemUIDeviceEntryFaceAuthInteractor(
+ context = applicationContext,
+ applicationScope = applicationCoroutineScope,
+ mainDispatcher = testDispatcher,
repository = deviceEntryFaceAuthRepository,
+ primaryBouncerInteractor = { primaryBouncerInteractor },
+ alternateBouncerInteractor = alternateBouncerInteractor,
+ keyguardTransitionInteractor = keyguardTransitionInteractor,
+ faceAuthenticationLogger = faceAuthLogger,
+ keyguardUpdateMonitor = keyguardUpdateMonitor,
+ deviceEntryFingerprintAuthRepository = deviceEntryFingerprintAuthRepository,
+ userRepository = userRepository,
+ facePropertyRepository = facePropertyRepository,
+ faceWakeUpTriggersConfig = faceWakeUpTriggersConfig,
+ powerInteractor = powerInteractor,
+ biometricSettingsRepository = biometricSettingsRepository,
+ trustManager = trustManager,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt
index 3d72967..599b5142 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.data.repository
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.kosmos.Kosmos
var Kosmos.deviceEntryFaceAuthRepository: DeviceEntryFaceAuthRepository by
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
index a1b6587..e96aeada 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
@@ -16,10 +16,11 @@
package com.android.systemui.keyguard.data.repository
-import com.android.keyguard.FaceAuthUiEvent
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
import dagger.Binds
import dagger.Module
import javax.inject.Inject
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
index 3cabf0c..5f5d428 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
@@ -27,6 +27,7 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.ui.BouncerView
import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -73,7 +74,7 @@
trustRepository,
testScope.backgroundScope,
mock(SelectedUserInteractor::class.java),
- mock(KeyguardFaceAuthInteractor::class.java),
+ mock(DeviceEntryFaceAuthInteractor::class.java),
)
val alternateBouncerInteractor =
AlternateBouncerInteractor(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index 9f71161..09ab655 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -52,20 +52,20 @@
import com.android.systemui.communal.data.repository.FakeCommunalRepository
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory
+import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.doze.DozeLogger
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.data.repository.TrustRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.kosmos.Kosmos
@@ -266,14 +266,14 @@
fun bouncerInteractor(
authenticationInteractor: AuthenticationInteractor,
- keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor = mock(),
+ deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor = mock(),
): BouncerInteractor {
return BouncerInteractor(
applicationScope = applicationScope(),
applicationContext = context,
repository = bouncerRepository,
authenticationInteractor = authenticationInteractor,
- keyguardFaceAuthInteractor = keyguardFaceAuthInteractor,
+ deviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor,
falsingInteractor = falsingInteractor(),
powerInteractor = powerInteractor(),
simBouncerInteractor = simBouncerInteractor,
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index 6b0fdb5..cf414d1 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -240,10 +240,20 @@
mEventInternal = Optional.of(new PresentationStatsEventInternal());
}
+ /**
+ * Set request_id
+ */
public void maybeSetRequestId(int requestId) {
mEventInternal.ifPresent(event -> event.mRequestId = requestId);
}
+ /**
+ * Set is_credential_request
+ */
+ public void maybeSetIsCredentialRequest(boolean isCredentialRequest) {
+ mEventInternal.ifPresent(event -> event.mIsCredentialRequest = isCredentialRequest);
+ }
+
public void maybeSetNoPresentationEventReason(@NotShownReason int reason) {
mEventInternal.ifPresent(event -> {
if (event.mCountShown == 0) {
@@ -567,7 +577,8 @@
+ " mSelectedDatasetPickedReason=" + event.mSelectedDatasetPickedReason
+ " mDetectionPreference=" + event.mDetectionPreference
+ " mFieldClassificationRequestId=" + event.mFieldClassificationRequestId
- + " mAppPackageUid=" + mCallingAppUid);
+ + " mAppPackageUid=" + mCallingAppUid
+ + " mIsCredentialRequest=" + event.mIsCredentialRequest);
}
// TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
@@ -606,7 +617,8 @@
event.mSelectedDatasetPickedReason,
event.mDetectionPreference,
event.mFieldClassificationRequestId,
- mCallingAppUid);
+ mCallingAppUid,
+ event.mIsCredentialRequest);
mEventInternal = Optional.empty();
}
@@ -640,6 +652,7 @@
@DatasetPickedReason int mSelectedDatasetPickedReason = PICK_REASON_UNKNOWN;
@DetectionPreference int mDetectionPreference = DETECTION_PREFER_UNKNOWN;
int mFieldClassificationRequestId = -1;
+ boolean mIsCredentialRequest = false;
PresentationStatsEventInternal() {}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index a49f9db..007be05 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1290,7 +1290,9 @@
Slog.v(TAG, "Requesting structure for request #" + ordinal + " ,requestId=" + requestId
+ ", flags=" + flags);
}
+ boolean isCredmanRequested = (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0;
mPresentationStatsEventLogger.maybeSetRequestId(requestId);
+ mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested);
mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
mFieldClassificationIdSnapshot);
mFillRequestEventLogger.maybeSetRequestId(requestId);
@@ -4360,8 +4362,10 @@
}
if (viewState.getResponse() != null) {
+ boolean isCredmanRequested = (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0;
FillResponse response = viewState.getResponse();
mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId());
+ mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested);
mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
mFieldClassificationIdSnapshot);
mPresentationStatsEventLogger.maybeSetAvailableCount(
diff --git a/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java b/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java
new file mode 100644
index 0000000..a7dbd1c
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2024 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.companion;
+
+import static android.os.UserHandle.getCallingUserId;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.UserIdInt;
+import android.companion.AssociationInfo;
+import android.companion.Flags;
+import android.companion.datatransfer.SystemDataTransferRequest;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.util.CollectionUtils;
+import com.android.server.companion.datatransfer.SystemDataTransferRequestStore;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Predicate;
+
+@SuppressLint("LongLogTag")
+class BackupRestoreProcessor {
+ static final String TAG = "CDM_BackupRestoreProcessor";
+ private static final int BACKUP_AND_RESTORE_VERSION = 0;
+
+ @NonNull
+ private final CompanionDeviceManagerService mService;
+ @NonNull
+ private final PackageManagerInternal mPackageManager;
+ @NonNull
+ private final AssociationStoreImpl mAssociationStore;
+ @NonNull
+ private final PersistentDataStore mPersistentStore;
+ @NonNull
+ private final SystemDataTransferRequestStore mSystemDataTransferRequestStore;
+ @NonNull
+ private final AssociationRequestsProcessor mAssociationRequestsProcessor;
+
+ BackupRestoreProcessor(@NonNull CompanionDeviceManagerService service,
+ @NonNull AssociationStoreImpl associationStore,
+ @NonNull PersistentDataStore persistentStore,
+ @NonNull SystemDataTransferRequestStore systemDataTransferRequestStore,
+ @NonNull AssociationRequestsProcessor associationRequestsProcessor) {
+ mService = service;
+ mPackageManager = service.mPackageManagerInternal;
+ mAssociationStore = associationStore;
+ mPersistentStore = persistentStore;
+ mSystemDataTransferRequestStore = systemDataTransferRequestStore;
+ mAssociationRequestsProcessor = associationRequestsProcessor;
+ }
+
+ /**
+ * Generate CDM state payload to be backed up.
+ * Backup payload is formatted as following:
+ * | (4) payload version | (4) AssociationInfo length | AssociationInfo XML
+ * | (4) SystemDataTransferRequest length | SystemDataTransferRequest XML (without userId)|
+ */
+ byte[] getBackupPayload(int userId) {
+ // Persist state first to generate an up-to-date XML file
+ mService.persistStateForUser(userId);
+ byte[] associationsPayload = mPersistentStore.getBackupPayload(userId);
+ int associationsPayloadLength = associationsPayload.length;
+
+ // System data transfer requests are persisted up-to-date already
+ byte[] requestsPayload = mSystemDataTransferRequestStore.getBackupPayload(userId);
+ int requestsPayloadLength = requestsPayload.length;
+
+ int payloadSize = /* 3 integers */ 12
+ + associationsPayloadLength
+ + requestsPayloadLength;
+
+ return ByteBuffer.allocate(payloadSize)
+ .putInt(BACKUP_AND_RESTORE_VERSION)
+ .putInt(associationsPayloadLength)
+ .put(associationsPayload)
+ .putInt(requestsPayloadLength)
+ .put(requestsPayload)
+ .array();
+ }
+
+ /**
+ * Create new associations and system data transfer request consents using backed up payload.
+ */
+ void applyRestoredPayload(byte[] payload, int userId) {
+ ByteBuffer buffer = ByteBuffer.wrap(payload);
+
+ // Make sure that payload version matches current version to ensure proper deserialization
+ int version = buffer.getInt();
+ if (version != BACKUP_AND_RESTORE_VERSION) {
+ Slog.e(TAG, "Unsupported backup payload version");
+ return;
+ }
+
+ // Read the bytes containing backed-up associations
+ byte[] associationsPayload = new byte[buffer.getInt()];
+ buffer.get(associationsPayload);
+ final Set<AssociationInfo> restoredAssociations = new HashSet<>();
+ mPersistentStore.readStateFromPayload(associationsPayload, userId,
+ restoredAssociations, new HashMap<>());
+
+ // Read the bytes containing backed-up system data transfer requests user consent
+ byte[] requestsPayload = new byte[buffer.getInt()];
+ buffer.get(requestsPayload);
+ List<SystemDataTransferRequest> restoredRequestsForUser =
+ mSystemDataTransferRequestStore.readRequestsFromPayload(requestsPayload);
+
+ // Get a list of installed packages ahead of time.
+ List<ApplicationInfo> installedApps = mPackageManager.getInstalledApplications(
+ 0, userId, getCallingUserId());
+
+ // Restored device may have a different user ID than the backed-up user's user-ID. Since
+ // association ID is dependent on the user ID, restored associations must account for
+ // this potential difference on their association IDs.
+ for (AssociationInfo restored : restoredAssociations) {
+ // Don't restore a revoked association. Since they weren't added to the device being
+ // restored in the first place, there is no need to worry about revoking a role that
+ // was never granted either.
+ if (restored.isRevoked()) {
+ continue;
+ }
+
+ // Filter restored requests for those that belong to the restored association.
+ List<SystemDataTransferRequest> restoredRequests = CollectionUtils.filter(
+ restoredRequestsForUser, it -> it.getAssociationId() == restored.getId());
+
+ // Handle collision: If a local association belonging to the same package already exists
+ // and their tags match, then keep the local one in favor of creating a new association.
+ if (handleCollision(userId, restored, restoredRequests)) {
+ continue;
+ }
+
+ // Create a new association reassigned to this user and a valid association ID
+ final String packageName = restored.getPackageName();
+ final int newId = mService.getNewAssociationIdForPackage(userId, packageName);
+ AssociationInfo newAssociation =
+ new AssociationInfo.Builder(newId, userId, packageName, restored)
+ .build();
+
+ // Check if the companion app for this association is already installed, then do one
+ // of the following:
+ // (1) If the app is already installed, then go ahead and add this association and grant
+ // the role attached to this association to the app.
+ // (2) If the app isn't yet installed, then add this association to the list of pending
+ // associations to be added when the package is installed in the future.
+ boolean isPackageInstalled = installedApps.stream()
+ .anyMatch(app -> packageName.equals(app.packageName));
+ if (isPackageInstalled) {
+ mAssociationRequestsProcessor.maybeGrantRoleAndStoreAssociation(newAssociation,
+ null, null);
+ } else {
+ // TODO(b/314992577): Check if package is installed before granting
+ }
+
+ // Re-map restored system data transfer requests to newly created associations
+ for (SystemDataTransferRequest restoredRequest : restoredRequests) {
+ SystemDataTransferRequest newRequest = restoredRequest.copyWithNewId(newId);
+ newRequest.setUserId(userId);
+ mSystemDataTransferRequestStore.writeRequest(userId, newRequest);
+ }
+ }
+
+ // Persist restored state.
+ mService.persistStateForUser(userId);
+ }
+
+ /**
+ * Detects and handles collision between restored association and local association. Returns
+ * true if there has been a collision and false otherwise.
+ */
+ private boolean handleCollision(@UserIdInt int userId,
+ AssociationInfo restored,
+ List<SystemDataTransferRequest> restoredRequests) {
+ List<AssociationInfo> localAssociations = mAssociationStore.getAssociationsForPackage(
+ restored.getUserId(), restored.getPackageName());
+ Predicate<AssociationInfo> isSameDevice = associationInfo -> {
+ boolean matchesMacAddress = Objects.equals(
+ associationInfo.getDeviceMacAddress(),
+ restored.getDeviceMacAddress());
+ boolean matchesTag = !Flags.associationTag()
+ || Objects.equals(associationInfo.getTag(), restored.getTag());
+ return matchesMacAddress && matchesTag;
+ };
+ AssociationInfo local = CollectionUtils.find(localAssociations, isSameDevice);
+
+ // No collision detected
+ if (local == null) {
+ return false;
+ }
+
+ Log.d(TAG, "Conflict detected with association id=" + local.getId()
+ + " while restoring CDM backup. Keeping local association.");
+
+ List<SystemDataTransferRequest> localRequests = mSystemDataTransferRequestStore
+ .readRequestsByAssociationId(userId, local.getId());
+
+ // If local association doesn't have any existing system data transfer request of same type
+ // attached, then restore corresponding request onto the local association. Otherwise, keep
+ // the locally stored request.
+ for (SystemDataTransferRequest restoredRequest : restoredRequests) {
+ boolean requestTypeExists = CollectionUtils.any(localRequests, request ->
+ request.getDataType() == restoredRequest.getDataType());
+
+ // This type of request consent already exists for the association.
+ if (requestTypeExists) {
+ continue;
+ }
+
+ Log.d(TAG, "Restoring " + restoredRequest.getClass().getSimpleName()
+ + " to an existing association id=" + local.getId() + ".");
+
+ SystemDataTransferRequest newRequest =
+ restoredRequest.copyWithNewId(local.getId());
+ newRequest.setUserId(userId);
+ mSystemDataTransferRequestStore.writeRequest(userId, newRequest);
+ }
+
+ return true;
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 487b66c..858887a 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -164,6 +164,7 @@
private final SystemDataTransferRequestStore mSystemDataTransferRequestStore;
private AssociationRequestsProcessor mAssociationRequestsProcessor;
private SystemDataTransferProcessor mSystemDataTransferProcessor;
+ private BackupRestoreProcessor mBackupRestoreProcessor;
private CompanionDevicePresenceMonitor mDevicePresenceMonitor;
private CompanionApplicationController mCompanionAppController;
private CompanionTransportManager mTransportManager;
@@ -256,6 +257,9 @@
mSystemDataTransferProcessor = new SystemDataTransferProcessor(this,
mPackageManagerInternal, mAssociationStore,
mSystemDataTransferRequestStore, mTransportManager);
+ mBackupRestoreProcessor = new BackupRestoreProcessor(
+ /* cdmService */ this, mAssociationStore, mPersistentStore,
+ mSystemDataTransferRequestStore, mAssociationRequestsProcessor);
// TODO(b/279663946): move context sync to a dedicated system service
mCrossDeviceSyncController = new CrossDeviceSyncController(getContext(), mTransportManager);
@@ -501,7 +505,7 @@
updateAtm(userId, updatedAssociations);
}
- private void persistStateForUser(@UserIdInt int userId) {
+ void persistStateForUser(@UserIdInt int userId) {
// We want to store both active associations and the revoked (removed) association that we
// are keeping around for the final clean-up (delayed role holder removal).
final List<AssociationInfo> allAssociations;
@@ -577,6 +581,11 @@
mCompanionAppController.onPackagesChanged(userId);
}
+ private void onPackageAddedInternal(@UserIdInt int userId, @NonNull String packageName) {
+ if (DEBUG) Log.i(TAG, "onPackageAddedInternal() u" + userId + "/" + packageName);
+ // TODO(b/314992577): Retroactively grant roles for restored associations
+ }
+
// Revoke associations if the selfManaged companion device does not connect for 3 months.
void removeInactiveSelfManagedAssociations() {
final long currentTime = System.currentTimeMillis();
@@ -1052,13 +1061,14 @@
@Override
public byte[] getBackupPayload(int userId) {
- // TODO(b/286124853): back up CDM data
- return new byte[0];
+ Log.i(TAG, "getBackupPayload() userId=" + userId);
+ return mBackupRestoreProcessor.getBackupPayload(userId);
}
@Override
public void applyRestoredPayload(byte[] payload, int userId) {
- // TODO(b/286124853): restore CDM data
+ Log.i(TAG, "applyRestoredPayload() userId=" + userId);
+ mBackupRestoreProcessor.applyRestoredPayload(payload, userId);
}
@Override
@@ -1067,7 +1077,8 @@
@NonNull String[] args) {
return new CompanionDeviceShellCommand(CompanionDeviceManagerService.this,
mAssociationStore, mDevicePresenceMonitor, mTransportManager,
- mSystemDataTransferProcessor, mAssociationRequestsProcessor)
+ mSystemDataTransferProcessor, mAssociationRequestsProcessor,
+ mBackupRestoreProcessor)
.exec(this, in.getFileDescriptor(), out.getFileDescriptor(),
err.getFileDescriptor(), args);
}
@@ -1499,6 +1510,11 @@
public void onPackageModified(String packageName) {
onPackageModifiedInternal(getChangingUserId(), packageName);
}
+
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ onPackageAddedInternal(getChangingUserId(), packageName);
+ }
};
static int getFirstAssociationIdForUser(@UserIdInt int userId) {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 9fdf5c2..53c0184c 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -18,6 +18,8 @@
import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_CONTEXT_SYNC;
+import static com.android.server.companion.PermissionsUtils.sanitizeWithCallerChecks;
+
import android.companion.AssociationInfo;
import android.companion.ContextSyncMessage;
import android.companion.Flags;
@@ -26,6 +28,7 @@
import android.net.MacAddress;
import android.os.Binder;
import android.os.ShellCommand;
+import android.util.Base64;
import android.util.proto.ProtoOutputStream;
import com.android.server.companion.datatransfer.SystemDataTransferProcessor;
@@ -47,19 +50,22 @@
private final SystemDataTransferProcessor mSystemDataTransferProcessor;
private final AssociationRequestsProcessor mAssociationRequestsProcessor;
+ private final BackupRestoreProcessor mBackupRestoreProcessor;
CompanionDeviceShellCommand(CompanionDeviceManagerService service,
AssociationStoreImpl associationStore,
CompanionDevicePresenceMonitor devicePresenceMonitor,
CompanionTransportManager transportManager,
SystemDataTransferProcessor systemDataTransferProcessor,
- AssociationRequestsProcessor associationRequestsProcessor) {
+ AssociationRequestsProcessor associationRequestsProcessor,
+ BackupRestoreProcessor backupRestoreProcessor) {
mService = service;
mAssociationStore = associationStore;
mDevicePresenceMonitor = devicePresenceMonitor;
mTransportManager = transportManager;
mSystemDataTransferProcessor = systemDataTransferProcessor;
mAssociationRequestsProcessor = associationRequestsProcessor;
+ mBackupRestoreProcessor = backupRestoreProcessor;
}
@Override
@@ -111,6 +117,19 @@
}
break;
+ case "disassociate-all": {
+ final int userId = getNextIntArgRequired();
+ final String packageName = getNextArgRequired();
+ final List<AssociationInfo> userAssociations =
+ mAssociationStore.getAssociationsForPackage(userId, packageName);
+ for (AssociationInfo association : userAssociations) {
+ if (sanitizeWithCallerChecks(mService.getContext(), association) != null) {
+ mService.disassociateInternal(association.getId());
+ }
+ }
+ }
+ break;
+
case "clear-association-memory-cache":
mService.persistState();
mService.loadAssociationsFromDisk();
@@ -126,6 +145,20 @@
mDevicePresenceMonitor.simulateDeviceEvent(associationId, /* event */ 1);
break;
+ case "get-backup-payload": {
+ final int userId = getNextIntArgRequired();
+ byte[] payload = mBackupRestoreProcessor.getBackupPayload(userId);
+ out.println(Base64.encodeToString(payload, Base64.NO_WRAP));
+ }
+ break;
+
+ case "apply-restored-payload": {
+ final int userId = getNextIntArgRequired();
+ byte[] payload = Base64.decode(getNextArgRequired(), Base64.NO_WRAP);
+ mBackupRestoreProcessor.applyRestoredPayload(payload, userId);
+ }
+ break;
+
case "remove-inactive-associations": {
// This command should trigger the same "clean-up" job as performed by the
// InactiveAssociationsRemovalService JobService. However, since the
@@ -355,6 +388,8 @@
pw.println(" Create a new Association.");
pw.println(" disassociate USER_ID PACKAGE MAC_ADDRESS");
pw.println(" Remove an existing Association.");
+ pw.println(" disassociate-all USER_ID");
+ pw.println(" Remove all Associations for a user.");
pw.println(" clear-association-memory-cache");
pw.println(" Clear the in-memory association cache and reload all association ");
pw.println(" information from persistent storage. USE FOR DEBUGGING PURPOSES ONLY.");
@@ -378,6 +413,14 @@
pw.println(" invoked for the same device (same ASSOCIATION_ID) no longer than");
pw.println(" 60 seconds ago.");
+ pw.println(" get-backup-payload USER_ID");
+ pw.println(" Generate backup payload for the given user and print its content");
+ pw.println(" encoded to a Base64 string.");
+ pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
+ pw.println(" apply-restored-payload USER_ID PAYLOAD");
+ pw.println(" Apply restored backup payload for the given user.");
+ pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
+
if (Flags.devicePresence()) {
pw.println(" simulate-device-event ASSOCIATION_ID EVENT");
pw.println(" Simulate the companion device event changes:");
diff --git a/services/companion/java/com/android/server/companion/DataStoreUtils.java b/services/companion/java/com/android/server/companion/DataStoreUtils.java
index c182529..04ce1f6 100644
--- a/services/companion/java/com/android/server/companion/DataStoreUtils.java
+++ b/services/companion/java/com/android/server/companion/DataStoreUtils.java
@@ -30,8 +30,11 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.IOException;
/**
* Util class for CDM data stores
@@ -88,6 +91,29 @@
}
}
+ /**
+ * Read a file and return the byte array containing the bytes of the file.
+ */
+ @NonNull
+ public static byte[] fileToByteArray(@NonNull AtomicFile file) {
+ if (!file.getBaseFile().exists()) {
+ Slog.d(TAG, "File does not exist");
+ return new byte[0];
+ }
+ try (FileInputStream in = file.openRead()) {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int read;
+ while ((read = in.read(buffer)) != -1) {
+ bytes.write(buffer, 0, read);
+ }
+ return bytes.toByteArray();
+ } catch (IOException e) {
+ Slog.e(TAG, "Error while reading requests file", e);
+ }
+ return new byte[0];
+ }
+
private DataStoreUtils() {
}
}
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index b4b9379..dbaf7e8 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -28,6 +28,7 @@
import static com.android.server.companion.CompanionDeviceManagerService.getFirstAssociationIdForUser;
import static com.android.server.companion.CompanionDeviceManagerService.getLastAssociationIdForUser;
import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.DataStoreUtils.fileToByteArray;
import static com.android.server.companion.DataStoreUtils.isEndOfTag;
import static com.android.server.companion.DataStoreUtils.isStartOfTag;
import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
@@ -55,9 +56,11 @@
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
@@ -328,34 +331,42 @@
@NonNull String rootTag, @Nullable Collection<AssociationInfo> associationsOut,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
try (FileInputStream in = file.openRead()) {
- final TypedXmlPullParser parser = Xml.resolvePullParser(in);
-
- XmlUtils.beginDocument(parser, rootTag);
- final int version = readIntAttribute(parser, XML_ATTR_PERSISTENCE_VERSION, 0);
- switch (version) {
- case 0:
- readAssociationsV0(parser, userId, associationsOut);
- break;
- case 1:
- while (true) {
- parser.nextTag();
- if (isStartOfTag(parser, XML_TAG_ASSOCIATIONS)) {
- readAssociationsV1(parser, userId, associationsOut);
- } else if (isStartOfTag(parser, XML_TAG_PREVIOUSLY_USED_IDS)) {
- readPreviouslyUsedIdsV1(parser, previouslyUsedIdsPerPackageOut);
- } else if (isEndOfTag(parser, rootTag)) {
- break;
- }
- }
- break;
- }
- return version;
+ return readStateFromInputStream(userId, in, rootTag, associationsOut,
+ previouslyUsedIdsPerPackageOut);
} catch (XmlPullParserException | IOException e) {
Slog.e(TAG, "Error while reading associations file", e);
return -1;
}
}
+ private int readStateFromInputStream(@UserIdInt int userId, @NonNull InputStream in,
+ @NonNull String rootTag, @Nullable Collection<AssociationInfo> associationsOut,
+ @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut)
+ throws XmlPullParserException, IOException {
+ final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+
+ XmlUtils.beginDocument(parser, rootTag);
+ final int version = readIntAttribute(parser, XML_ATTR_PERSISTENCE_VERSION, 0);
+ switch (version) {
+ case 0:
+ readAssociationsV0(parser, userId, associationsOut);
+ break;
+ case 1:
+ while (true) {
+ parser.nextTag();
+ if (isStartOfTag(parser, XML_TAG_ASSOCIATIONS)) {
+ readAssociationsV1(parser, userId, associationsOut);
+ } else if (isStartOfTag(parser, XML_TAG_PREVIOUSLY_USED_IDS)) {
+ readPreviouslyUsedIdsV1(parser, previouslyUsedIdsPerPackageOut);
+ } else if (isEndOfTag(parser, rootTag)) {
+ break;
+ }
+ }
+ break;
+ }
+ return version;
+ }
+
private void persistStateToFileLocked(@NonNull AtomicFile file,
@Nullable Collection<AssociationInfo> associations,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) {
@@ -391,6 +402,26 @@
u -> createStorageFileForUser(userId, FILE_NAME));
}
+ byte[] getBackupPayload(@UserIdInt int userId) {
+ Slog.i(TAG, "Fetching stored state data for user " + userId + " from disk");
+ final AtomicFile file = getStorageFileForUser(userId);
+
+ synchronized (file) {
+ return fileToByteArray(file);
+ }
+ }
+
+ void readStateFromPayload(byte[] payload, @UserIdInt int userId,
+ @NonNull Set<AssociationInfo> associationsOut,
+ @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
+ try (ByteArrayInputStream in = new ByteArrayInputStream(payload)) {
+ readStateFromInputStream(userId, in, XML_TAG_STATE, associationsOut,
+ previouslyUsedIdsPerPackageOut);
+ } catch (XmlPullParserException | IOException e) {
+ Slog.e(TAG, "Error while reading associations file", e);
+ }
+ }
+
private static @NonNull File getBaseLegacyStorageFileForUser(@UserIdInt int userId) {
return new File(Environment.getUserSystemDirectory(userId), FILE_NAME_LEGACY);
}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
index 9f489e8..8fe0454 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
@@ -23,6 +23,7 @@
import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.DataStoreUtils.fileToByteArray;
import static com.android.server.companion.DataStoreUtils.isEndOfTag;
import static com.android.server.companion.DataStoreUtils.isStartOfTag;
import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
@@ -44,6 +45,7 @@
import org.xmlpull.v1.XmlPullParserException;
+import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
@@ -152,6 +154,33 @@
mExecutor.execute(() -> writeRequestsToStore(userId, cachedRequests));
}
+ /**
+ * Return the byte contents of the XML file storing current system data transfer requests.
+ */
+ public byte[] getBackupPayload(@UserIdInt int userId) {
+ final AtomicFile file = getStorageFileForUser(userId);
+
+ synchronized (file) {
+ return fileToByteArray(file);
+ }
+ }
+
+ /**
+ * Parse the byte array containing XML information of system data transfer requests into
+ * an array list of requests.
+ */
+ public List<SystemDataTransferRequest> readRequestsFromPayload(byte[] payload) {
+ try (ByteArrayInputStream in = new ByteArrayInputStream(payload)) {
+ final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+ XmlUtils.beginDocument(parser, XML_TAG_REQUESTS);
+
+ return readRequestsFromXml(parser);
+ } catch (XmlPullParserException | IOException e) {
+ Slog.e(LOG_TAG, "Error while reading requests file", e);
+ return new ArrayList<>();
+ }
+ }
+
@GuardedBy("mLock")
private ArrayList<SystemDataTransferRequest> readRequestsFromCache(@UserIdInt int userId) {
ArrayList<SystemDataTransferRequest> cachedRequests = mCachedPerUser.get(userId);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 39b8643..19a9239 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3236,7 +3236,7 @@
super.createUserStorageKeys_enforcePermission();
try {
- mVold.createUserStorageKeys(userId, serialNumber, ephemeral);
+ mVold.createUserStorageKeys(userId, ephemeral);
// Since the user's CE key was just created, the user's CE storage is now unlocked.
synchronized (mLock) {
mCeUnlockedUsers.append(userId);
@@ -3281,7 +3281,7 @@
super.unlockCeStorage_enforcePermission();
if (StorageManager.isFileEncrypted()) {
- mVold.unlockCeStorage(userId, serialNumber, HexDump.toHexString(secret));
+ mVold.unlockCeStorage(userId, HexDump.toHexString(secret));
}
synchronized (mLock) {
mCeUnlockedUsers.append(userId);
@@ -3368,7 +3368,7 @@
private void prepareUserStorageInternal(String volumeUuid, int userId, int serialNumber,
int flags) throws Exception {
try {
- mVold.prepareUserStorage(volumeUuid, userId, serialNumber, flags);
+ mVold.prepareUserStorage(volumeUuid, userId, flags);
// After preparing user storage, we should check if we should mount data mirror again,
// and we do it for user 0 only as we only need to do once for all users.
if (volumeUuid != null) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 7ac4dd3..a1b6f29 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -7879,7 +7879,6 @@
DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_A2DP_SET);
DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_BLE_SET);
DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_USB_SET);
- DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_HDMI);
}
/** only public for mocking/spying, do not call outside of AudioService */
diff --git a/services/core/java/com/android/server/audio/FadeOutManager.java b/services/core/java/com/android/server/audio/FadeOutManager.java
index cbcd8f5..4d5bce5 100644
--- a/services/core/java/com/android/server/audio/FadeOutManager.java
+++ b/services/core/java/com/android/server/audio/FadeOutManager.java
@@ -29,6 +29,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.utils.EventLogger;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -345,7 +346,8 @@
}
if (apc.getPlayerProxy() != null) {
applyVolumeShaperInternal(apc, piid, volShaper,
- skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED);
+ skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED, skipRamp,
+ PlaybackActivityMonitor.EVENT_TYPE_FADE_OUT);
mFadedPlayers.put(piid, volShaper);
} else {
if (DEBUG) {
@@ -361,7 +363,8 @@
final AudioPlaybackConfiguration apc = players.get(piid);
if ((apc != null) && (apc.getPlayerProxy() != null)) {
applyVolumeShaperInternal(apc, piid, /* volShaperConfig= */ null,
- VolumeShaper.Operation.REVERSE);
+ VolumeShaper.Operation.REVERSE, /* skipRamp= */ false,
+ PlaybackActivityMonitor.EVENT_TYPE_FADE_IN);
} else {
// this piid was in the list of faded players, but wasn't found
if (DEBUG) {
@@ -373,6 +376,7 @@
mFadedPlayers.clear();
}
+ @GuardedBy("mLock")
void fadeInPlayer(@NonNull AudioPlaybackConfiguration apc,
@Nullable VolumeShaper.Configuration config) {
int piid = Integer.valueOf(apc.getPlayerInterfaceId());
@@ -385,10 +389,17 @@
return;
}
+ VolumeShaper.Operation operation = VolumeShaper.Operation.REVERSE;
+ if (config != null) {
+ // replace and join the volumeshapers with (possibly) in progress fade out operation
+ // for a smoother fade in
+ operation = new VolumeShaper.Operation.Builder()
+ .replace(mFadedPlayers.get(piid).getId(), /* join= */ true).build();
+ }
mFadedPlayers.remove(piid);
if (apc.getPlayerProxy() != null) {
- applyVolumeShaperInternal(apc, piid, config,
- config != null ? PLAY_CREATE_IF_NEEDED : VolumeShaper.Operation.REVERSE);
+ applyVolumeShaperInternal(apc, piid, config, operation, /* skipRamp= */ false,
+ PlaybackActivityMonitor.EVENT_TYPE_FADE_IN);
} else {
if (DEBUG) {
Slog.v(TAG, "Error fading in player piid:" + piid
@@ -397,6 +408,7 @@
}
}
+ @GuardedBy("mLock")
void clear() {
if (mFadedPlayers.size() > 0) {
if (DEBUG) {
@@ -413,21 +425,40 @@
}
private void applyVolumeShaperInternal(AudioPlaybackConfiguration apc, int piid,
- VolumeShaper.Configuration volShaperConfig, VolumeShaper.Operation operation) {
+ VolumeShaper.Configuration volShaperConfig, VolumeShaper.Operation operation,
+ boolean skipRamp, String eventType) {
VolumeShaper.Configuration config = volShaperConfig;
// when operation is reverse, use the fade out volume shaper config instead
if (operation.equals(VolumeShaper.Operation.REVERSE)) {
config = mFadedPlayers.get(piid);
}
try {
- PlaybackActivityMonitor.sEventLogger.enqueue(
- (new PlaybackActivityMonitor.FadeEvent(apc, config, operation))
- .printLog(TAG));
+ logFadeEvent(apc, piid, volShaperConfig, operation, skipRamp, eventType);
apc.getPlayerProxy().applyVolumeShaper(config, operation);
} catch (Exception e) {
- Slog.e(TAG, "Error fading player piid:" + piid + " uid:" + mUid
- + " operation:" + operation, e);
+ Slog.e(TAG, "Error " + eventType + " piid:" + piid + " uid:" + mUid, e);
}
}
+
+ private void logFadeEvent(AudioPlaybackConfiguration apc, int piid,
+ VolumeShaper.Configuration config, VolumeShaper.Operation operation,
+ boolean skipRamp, String eventType) {
+ if (eventType.equals(PlaybackActivityMonitor.EVENT_TYPE_FADE_OUT)) {
+ PlaybackActivityMonitor.sEventLogger.enqueue(
+ (new PlaybackActivityMonitor.FadeOutEvent(apc, skipRamp, config, operation))
+ .printLog(TAG));
+ return;
+ }
+
+ if (eventType.equals(PlaybackActivityMonitor.EVENT_TYPE_FADE_IN)) {
+ PlaybackActivityMonitor.sEventLogger.enqueue(
+ (new PlaybackActivityMonitor.FadeInEvent(apc, skipRamp, config, operation))
+ .printLog(TAG));
+ return;
+ }
+
+ PlaybackActivityMonitor.sEventLogger.enqueue(
+ (new EventLogger.StringEvent(eventType + " piid:" + piid)).printLog(TAG));
+ }
}
}
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index e69fbbd..08da32e 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -84,6 +84,8 @@
/*package*/ static final int VOLUME_SHAPER_SYSTEM_FADEOUT_ID = 2;
/*package*/ static final int VOLUME_SHAPER_SYSTEM_MUTE_AWAIT_CONNECTION_ID = 3;
/*package*/ static final int VOLUME_SHAPER_SYSTEM_STRONG_DUCK_ID = 4;
+ /*package*/ static final String EVENT_TYPE_FADE_OUT = "fading out";
+ /*package*/ static final String EVENT_TYPE_FADE_IN = "fading in";
// ducking settings for a "normal duck" at -14dB
private static final VolumeShaper.Configuration DUCK_VSHAPE =
@@ -1204,11 +1206,13 @@
return;
}
try {
- sEventLogger.enqueue((new DuckEvent(apc, skipRamp, mUseStrongDuck))
- .printLog(TAG));
- apc.getPlayerProxy().applyVolumeShaper(
- mUseStrongDuck ? STRONG_DUCK_VSHAPE : DUCK_VSHAPE,
- skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED);
+ VolumeShaper.Configuration config =
+ mUseStrongDuck ? STRONG_DUCK_VSHAPE : DUCK_VSHAPE;
+ VolumeShaper.Operation operation =
+ skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED;
+ sEventLogger.enqueue((new DuckEvent(apc, skipRamp, mUseStrongDuck, config,
+ operation)).printLog(TAG));
+ apc.getPlayerProxy().applyVolumeShaper(config, operation);
mDuckedPlayers.add(piid);
} catch (Exception e) {
Log.e(TAG, "Error ducking player piid:" + piid + " uid:" + mUid, e);
@@ -1363,58 +1367,41 @@
}
}
- static final class FadeEvent extends EventLogger.Event {
- private final int mPlayerIId;
- private final int mPlayerType;
- private final int mClientUid;
- private final int mClientPid;
- private final AudioAttributes mPlayerAttr;
- private final VolumeShaper.Configuration mVShaper;
- private final VolumeShaper.Operation mVOperation;
-
- FadeEvent(AudioPlaybackConfiguration apc, VolumeShaper.Configuration vShaper,
- VolumeShaper.Operation vOperation) {
- mPlayerIId = apc.getPlayerInterfaceId();
- mClientUid = apc.getClientUid();
- mClientPid = apc.getClientPid();
- mPlayerAttr = apc.getAudioAttributes();
- mPlayerType = apc.getPlayerType();
- mVShaper = vShaper;
- mVOperation = vOperation;
- }
-
- @Override
- public String eventToString() {
- return "Fade Event:" + " player piid:" + mPlayerIId
- + " uid/pid:" + mClientUid + "/" + mClientPid
- + " player type:"
- + AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType)
- + " attr:" + mPlayerAttr
- + " volume shaper:" + mVShaper
- + " volume operation:" + mVOperation;
- }
- }
-
private abstract static class VolumeShaperEvent extends EventLogger.Event {
private final int mPlayerIId;
private final boolean mSkipRamp;
private final int mClientUid;
private final int mClientPid;
+ private final int mPlayerType;
+ private final AudioAttributes mPlayerAttr;
+ private final VolumeShaper.Configuration mConfig;
+ private final VolumeShaper.Operation mOperation;
abstract String getVSAction();
- VolumeShaperEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
+ VolumeShaperEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp,
+ VolumeShaper.Configuration config, VolumeShaper.Operation operation) {
mPlayerIId = apc.getPlayerInterfaceId();
mSkipRamp = skipRamp;
mClientUid = apc.getClientUid();
mClientPid = apc.getClientPid();
+ mPlayerAttr = apc.getAudioAttributes();
+ mPlayerType = apc.getPlayerType();
+ mConfig = config;
+ mOperation = operation;
}
@Override
public String eventToString() {
- return new StringBuilder(getVSAction()).append(" player piid:").append(mPlayerIId)
- .append(" uid/pid:").append(mClientUid).append("/").append(mClientPid)
- .append(" skip ramp:").append(mSkipRamp).toString();
+ return getVSAction()
+ + " player piid:" + mPlayerIId
+ + " uid/pid:" + mClientUid + "/" + mClientPid
+ + " skip ramp:" + mSkipRamp
+ + " player type:"
+ + AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType)
+ + " attr:" + mPlayerAttr
+ + " config:" + mConfig
+ + " operation:" + mOperation;
}
}
@@ -1426,9 +1413,10 @@
return mUseStrongDuck ? "ducking (strong)" : "ducking";
}
- DuckEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp, boolean useStrongDuck)
+ DuckEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp, boolean useStrongDuck,
+ VolumeShaper.Configuration config, VolumeShaper.Operation operation)
{
- super(apc, skipRamp);
+ super(apc, skipRamp, config, operation);
mUseStrongDuck = useStrongDuck;
}
}
@@ -1436,11 +1424,24 @@
static final class FadeOutEvent extends VolumeShaperEvent {
@Override
String getVSAction() {
- return "fading out";
+ return EVENT_TYPE_FADE_OUT;
}
- FadeOutEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
- super(apc, skipRamp);
+ FadeOutEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp,
+ VolumeShaper.Configuration config, VolumeShaper.Operation operation) {
+ super(apc, skipRamp, config, operation);
+ }
+ }
+
+ static final class FadeInEvent extends VolumeShaperEvent {
+ @Override
+ String getVSAction() {
+ return EVENT_TYPE_FADE_IN;
+ }
+
+ FadeInEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp,
+ VolumeShaper.Configuration config, VolumeShaper.Operation operation) {
+ super(apc, skipRamp, config, operation);
}
}
diff --git a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
index 1ac3a12..e54f30f 100644
--- a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
+++ b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
@@ -27,6 +27,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.config.layout.Layouts;
import com.android.server.display.config.layout.XmlParser;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.layout.DisplayIdProducer;
import com.android.server.display.layout.Layout;
@@ -63,24 +64,40 @@
private static final String CONFIG_FILE_PATH =
"etc/displayconfig/display_layout_configuration.xml";
+ private static final String DATA_CONFIG_FILE_PATH =
+ "system/displayconfig/display_layout_configuration.xml";
+
private final SparseArray<Layout> mLayoutMap = new SparseArray<>();
private final DisplayIdProducer mIdProducer;
+ private final boolean mIsPortInDisplayLayoutEnabled;
- DeviceStateToLayoutMap(DisplayIdProducer idProducer) {
- this(idProducer, Environment.buildPath(
- Environment.getVendorDirectory(), CONFIG_FILE_PATH));
+ DeviceStateToLayoutMap(DisplayIdProducer idProducer, DisplayManagerFlags flags) {
+ this(idProducer, flags, getConfigFile());
}
- DeviceStateToLayoutMap(DisplayIdProducer idProducer, File configFile) {
+ DeviceStateToLayoutMap(DisplayIdProducer idProducer, DisplayManagerFlags flags,
+ File configFile) {
+ mIsPortInDisplayLayoutEnabled = flags.isPortInDisplayLayoutEnabled();
mIdProducer = idProducer;
loadLayoutsFromConfig(configFile);
createLayout(STATE_DEFAULT);
}
+ static private File getConfigFile() {
+ final File configFileFromDataDir = Environment.buildPath(Environment.getDataDirectory(),
+ DATA_CONFIG_FILE_PATH);
+ if (configFileFromDataDir.exists()) {
+ return configFileFromDataDir;
+ } else {
+ return Environment.buildPath(Environment.getVendorDirectory(), CONFIG_FILE_PATH);
+ }
+ }
+
public void dumpLocked(IndentingPrintWriter ipw) {
ipw.println("DeviceStateToLayoutMap:");
ipw.increaseIndent();
+ ipw.println("mIsPortInDisplayLayoutEnabled=" + mIsPortInDisplayLayoutEnabled);
ipw.println("Registered Layouts:");
for (int i = 0; i < mLayoutMap.size(); i++) {
ipw.println("state(" + mLayoutMap.keyAt(i) + "): " + mLayoutMap.valueAt(i));
@@ -120,13 +137,15 @@
final Layout layout = createLayout(state);
for (com.android.server.display.config.layout.Display d: l.getDisplay()) {
assert layout != null;
+ final DisplayAddress address = getDisplayAddressForLayoutDisplay(d);
+
int position = getPosition(d.getPosition());
BigInteger leadDisplayPhysicalId = d.getLeadDisplayAddress();
DisplayAddress leadDisplayAddress = leadDisplayPhysicalId == null ? null
: DisplayAddress.fromPhysicalDisplayId(
leadDisplayPhysicalId.longValue());
layout.createDisplayLocked(
- DisplayAddress.fromPhysicalDisplayId(d.getAddress().longValue()),
+ address,
d.isDefaultDisplay(),
d.isEnabled(),
d.getDisplayGroup(),
@@ -146,6 +165,20 @@
}
}
+ private DisplayAddress getDisplayAddressForLayoutDisplay(
+ @NonNull com.android.server.display.config.layout.Display display) {
+ BigInteger xmlAddress = display.getAddress_optional();
+ if (xmlAddress != null) {
+ return DisplayAddress.fromPhysicalDisplayId(xmlAddress.longValue());
+ }
+ if (!mIsPortInDisplayLayoutEnabled || display.getPort_optional() == null) {
+ throw new IllegalArgumentException(
+ "Must specify a display identifier in display layout configuration: " + display);
+ }
+ return DisplayAddress.fromPortAndModel((int) display.getPort_optional().longValue(),
+ /* model= */ null);
+ }
+
private int getPosition(@NonNull String position) {
int positionInt = POSITION_UNKNOWN;
if (FRONT_STRING.equals(position)) {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
index 67e612d..6164154 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
@@ -129,7 +129,9 @@
public DisplayDevice getByAddressLocked(@NonNull DisplayAddress address) {
for (int i = mDisplayDevices.size() - 1; i >= 0; i--) {
final DisplayDevice device = mDisplayDevices.get(i);
- if (address.equals(device.getDisplayDeviceInfoLocked().address)) {
+ final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ if (address.equals(info.address)
+ || DisplayAddress.Physical.isPortMatch(address, info.address)) {
return device;
}
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 115111a..2e8de31 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -205,7 +205,7 @@
@NonNull Handler handler, DisplayManagerFlags flags) {
this(context, foldSettingProvider, repo, listener, syncRoot, handler,
new DeviceStateToLayoutMap((isDefault) -> isDefault ? DEFAULT_DISPLAY
- : sNextNonDefaultDisplayId++), flags);
+ : sNextNonDefaultDisplayId++, flags), flags);
}
LogicalDisplayMapper(@NonNull Context context, FoldSettingProvider foldSettingProvider,
@@ -1094,8 +1094,8 @@
final DisplayAddress address = displayLayout.getAddress();
final DisplayDevice device = mDisplayDeviceRepo.getByAddressLocked(address);
if (device == null) {
- Slog.w(TAG, "The display device (" + address + "), is not available"
- + " for the display state " + mDeviceState);
+ Slog.w(TAG, "applyLayoutLocked: The display device (" + address + "), is not "
+ + "available for the display state " + mDeviceState);
continue;
}
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index aa7f07d..be48eb4 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -37,6 +37,9 @@
// 'adb shell setprop persist.log.tag.DisplayManagerFlags DEBUG && adb reboot'
private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
+ private final FlagState mPortInDisplayLayoutFlagState = new FlagState(
+ Flags.FLAG_ENABLE_PORT_IN_DISPLAY_LAYOUT,
+ Flags::enablePortInDisplayLayout);
private final FlagState mConnectedDisplayManagementFlagState = new FlagState(
Flags.FLAG_ENABLE_CONNECTED_DISPLAY_MANAGEMENT,
@@ -109,6 +112,18 @@
Flags.FLAG_FAST_HDR_TRANSITIONS,
Flags::fastHdrTransitions);
+ private final FlagState mRefreshRateVotingTelemetry = new FlagState(
+ Flags.FLAG_REFRESH_RATE_VOTING_TELEMETRY,
+ Flags::refreshRateVotingTelemetry
+ );
+
+ /**
+ * @return {@code true} if 'port' is allowed in display layout configuration file.
+ */
+ public boolean isPortInDisplayLayoutEnabled() {
+ return mPortInDisplayLayoutFlagState.isEnabled();
+ }
+
/** Returns whether connected display management is enabled or not. */
public boolean isConnectedDisplayManagementEnabled() {
return mConnectedDisplayManagementFlagState.isEnabled();
@@ -220,6 +235,10 @@
return mFastHdrTransitions.isEnabled();
}
+ public boolean isRefreshRateVotingTelemetryEnabled() {
+ return mRefreshRateVotingTelemetry.isEnabled();
+ }
+
/**
* dumps all flagstates
* @param pw printWriter
@@ -242,6 +261,7 @@
pw.println(" " + mBrightnessWearBedtimeModeClamperFlagState);
pw.println(" " + mAutoBrightnessModesFlagState);
pw.println(" " + mFastHdrTransitions);
+ pw.println(" " + mRefreshRateVotingTelemetry);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 04ecbb9..c9569cb 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -3,6 +3,14 @@
# Important: Flags must be accessed through DisplayManagerFlags.
flag {
+ name: "enable_port_in_display_layout"
+ namespace: "display_manager"
+ description: "Allows refering to displays by port in display layout"
+ bug: "303058435"
+ is_fixed_read_only: true
+}
+
+flag {
name: "enable_connected_display_management"
namespace: "display_manager"
description: "Feature flag for Connected Display management"
@@ -161,3 +169,10 @@
is_fixed_read_only: true
}
+flag {
+ name: "refresh_rate_voting_telemetry"
+ namespace: "display_manager"
+ description: "Feature flag for enabling telemetry for refresh rate voting in DisplayManager"
+ bug: "310029108"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/layout/Layout.java b/services/core/java/com/android/server/display/layout/Layout.java
index 40cb3303..8a362f7 100644
--- a/services/core/java/com/android/server/display/layout/Layout.java
+++ b/services/core/java/com/android/server/display/layout/Layout.java
@@ -200,13 +200,7 @@
* @return True if the specified address is used in this layout.
*/
public boolean contains(@NonNull DisplayAddress address) {
- final int size = mDisplays.size();
- for (int i = 0; i < size; i++) {
- if (address.equals(mDisplays.get(i).getAddress())) {
- return true;
- }
- }
- return false;
+ return getByAddress(address) != null;
}
/**
@@ -237,6 +231,9 @@
if (address.equals(display.getAddress())) {
return display;
}
+ if (DisplayAddress.Physical.isPortMatch(address, display.getAddress())) {
+ return display;
+ }
}
return null;
}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 6e503cb..50e9533 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -154,6 +154,9 @@
private final VotesStorage mVotesStorage;
+ @Nullable
+ private final VotesStatsReporter mVotesStatsReporter;
+
/**
* The allowed refresh rate switching type. This is used by SurfaceFlinger.
*/
@@ -204,6 +207,8 @@
mContext = context;
mHandler = new DisplayModeDirectorHandler(handler.getLooper());
mInjector = injector;
+ mVotesStatsReporter = injector.getVotesStatsReporter(
+ displayManagerFlags.isRefreshRateVotingTelemetryEnabled());
mSupportedModesByDisplay = new SparseArray<>();
mDefaultModeByDisplay = new SparseArray<>();
mAppRequestObserver = new AppRequestObserver();
@@ -214,7 +219,8 @@
mBrightnessObserver = new BrightnessObserver(context, handler, injector);
mDefaultDisplayDeviceConfig = null;
mUdfpsObserver = new UdfpsObserver();
- mVotesStorage = new VotesStorage(this::notifyDesiredDisplayModeSpecsChangedLocked);
+ mVotesStorage = new VotesStorage(this::notifyDesiredDisplayModeSpecsChangedLocked,
+ mVotesStatsReporter);
mDisplayObserver = new DisplayObserver(context, handler, mVotesStorage);
mSensorObserver = new SensorObserver(context, mVotesStorage, injector);
mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, mVotesStorage);
@@ -341,6 +347,11 @@
appRequestSummary.limitRefreshRanges(primarySummary);
Display.Mode baseMode = primarySummary.selectBaseMode(availableModes, defaultMode);
+ if (mVotesStatsReporter != null) {
+ mVotesStatsReporter.reportVotesActivated(displayId, lowestConsideredPriority,
+ baseMode, votes);
+ }
+
if (baseMode == null) {
Slog.w(TAG, "Can't find a set of allowed modes which satisfies the votes. Falling"
+ " back to the default mode. Display = " + displayId + ", votes = " + votes
@@ -970,9 +981,14 @@
if (!mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled) {
// The flag had been turned off, we need to restore the original value
- Settings.System.putFloatForUser(cr,
- Settings.System.MIN_REFRESH_RATE, minRefreshRate, cr.getUserId());
+ Settings.System.putFloatForUser(cr, Settings.System.MIN_REFRESH_RATE,
+ highestRefreshRate, cr.getUserId());
}
+ } else if (mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled
+ && Math.round(minRefreshRate) == Math.round(highestRefreshRate)) {
+ // The flag has been turned on, we need to upgrade the setting
+ Settings.System.putFloatForUser(cr, Settings.System.MIN_REFRESH_RATE,
+ Float.POSITIVE_INFINITY, cr.getUserId());
}
float peakRefreshRate = Settings.System.getFloatForUser(cr,
@@ -983,9 +999,14 @@
if (!mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled) {
// The flag had been turned off, we need to restore the original value
- Settings.System.putFloatForUser(cr,
- Settings.System.PEAK_REFRESH_RATE, peakRefreshRate, cr.getUserId());
+ Settings.System.putFloatForUser(cr, Settings.System.PEAK_REFRESH_RATE,
+ highestRefreshRate, cr.getUserId());
}
+ } else if (mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled
+ && Math.round(peakRefreshRate) == Math.round(highestRefreshRate)) {
+ // The flag has been turned on, we need to upgrade the setting
+ Settings.System.putFloatForUser(cr, Settings.System.PEAK_REFRESH_RATE,
+ Float.POSITIVE_INFINITY, cr.getUserId());
}
updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
@@ -2811,6 +2832,9 @@
StatusBarManagerInternal getStatusBarManagerInternal();
SensorManagerInternal getSensorManagerInternal();
+
+ @Nullable
+ VotesStatsReporter getVotesStatsReporter(boolean refreshRateVotingTelemetryEnabled);
}
@VisibleForTesting
@@ -2943,6 +2967,13 @@
return LocalServices.getService(SensorManagerInternal.class);
}
+ @Override
+ public VotesStatsReporter getVotesStatsReporter(boolean refreshRateVotingTelemetryEnabled) {
+ // if frame rate override supported, renderRates will be ignored in mode selection
+ return new VotesStatsReporter(supportsFrameRateOverride(),
+ refreshRateVotingTelemetryEnabled);
+ }
+
private DisplayManager getDisplayManager() {
if (mDisplayManager == null) {
mDisplayManager = mContext.getSystemService(DisplayManager.class);
diff --git a/services/core/java/com/android/server/display/mode/VotesStatsReporter.java b/services/core/java/com/android/server/display/mode/VotesStatsReporter.java
new file mode 100644
index 0000000..a30c4d2
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/VotesStatsReporter.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 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.display.mode;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Trace;
+import android.util.SparseArray;
+import android.view.Display;
+
+/**
+ * The VotesStatsReporter is responsible for collecting and sending Vote related statistics
+ */
+class VotesStatsReporter {
+ private static final String TAG = "VotesStatsReporter";
+ private static final int REFRESH_RATE_NOT_LIMITED = 1000;
+ private final boolean mIgnoredRenderRate;
+ private final boolean mFrameworkStatsLogReportingEnabled;
+
+ public VotesStatsReporter(boolean ignoreRenderRate, boolean refreshRateVotingTelemetryEnabled) {
+ mIgnoredRenderRate = ignoreRenderRate;
+ mFrameworkStatsLogReportingEnabled = refreshRateVotingTelemetryEnabled;
+ }
+
+ void reportVoteAdded(int displayId, int priority, @NonNull Vote vote) {
+ int maxRefreshRate = getMaxRefreshRate(vote, mIgnoredRenderRate);
+ Trace.traceCounter(Trace.TRACE_TAG_POWER,
+ TAG + "." + displayId + ":" + Vote.priorityToString(priority), maxRefreshRate);
+ // if ( mFrameworkStatsLogReportingEnabled) {
+ // FrameworkStatsLog.write(VOTE_CHANGED, displayID, priority, ADDED, maxRefreshRate, -1);
+ // }
+ }
+
+ void reportVoteRemoved(int displayId, int priority) {
+ Trace.traceCounter(Trace.TRACE_TAG_POWER,
+ TAG + "." + displayId + ":" + Vote.priorityToString(priority), -1);
+ // if ( mFrameworkStatsLogReportingEnabled) {
+ // FrameworkStatsLog.write(VOTE_CHANGED, displayID, priority, REMOVED, -1, -1);
+ // }
+ }
+
+ void reportVotesActivated(int displayId, int minPriority, @Nullable Display.Mode baseMode,
+ SparseArray<Vote> votes) {
+// if (!mFrameworkStatsLogReportingEnabled) {
+// return;
+// }
+// int selectedRefreshRate = baseMode != null ? (int) baseMode.getRefreshRate() : -1;
+// for (int priority = minPriority; priority <= Vote.MAX_PRIORITY; priority ++) {
+// Vote vote = votes.get(priority);
+// if (vote != null) {
+// int maxRefreshRate = getMaxRefreshRate(vote, mIgnoredRenderRate);
+// FrameworkStatsLog.write(VOTE_CHANGED, displayId, priority,
+// ACTIVE, maxRefreshRate, selectedRefreshRate);
+// }
+// }
+ }
+
+ private static int getMaxRefreshRate(@NonNull Vote vote, boolean ignoreRenderRate) {
+ int maxRefreshRate = REFRESH_RATE_NOT_LIMITED;
+ if (vote instanceof RefreshRateVote.PhysicalVote physicalVote) {
+ maxRefreshRate = (int) physicalVote.mMaxRefreshRate;
+ } else if (!ignoreRenderRate && (vote instanceof RefreshRateVote.RenderVote renderVote)) {
+ maxRefreshRate = (int) renderVote.mMaxRefreshRate;
+ } else if (vote instanceof SupportedModesVote supportedModesVote) {
+ // SupportedModesVote limits mode by specific refreshRates, so highest rr is allowed
+ maxRefreshRate = 0;
+ for (SupportedModesVote.SupportedMode mode : supportedModesVote.mSupportedModes) {
+ maxRefreshRate = Math.max(maxRefreshRate, (int) mode.mPeakRefreshRate);
+ }
+ } else if (vote instanceof CombinedVote combinedVote) {
+ for (Vote subVote: combinedVote.mVotes) {
+ // CombinedVote should not have CombinedVote in mVotes, so recursion depth will be 1
+ maxRefreshRate = Math.min(maxRefreshRate,
+ getMaxRefreshRate(subVote, ignoreRenderRate));
+ }
+ }
+ return maxRefreshRate;
+ }
+}
diff --git a/services/core/java/com/android/server/display/mode/VotesStorage.java b/services/core/java/com/android/server/display/mode/VotesStorage.java
index 95fb8fc..7a1f7e9 100644
--- a/services/core/java/com/android/server/display/mode/VotesStorage.java
+++ b/services/core/java/com/android/server/display/mode/VotesStorage.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.Trace;
import android.util.Slog;
import android.util.SparseArray;
@@ -38,6 +37,9 @@
private final Listener mListener;
+ @Nullable
+ private final VotesStatsReporter mVotesStatsReporter;
+
private final Object mStorageLock = new Object();
// A map from the display ID to the collection of votes and their priority. The latter takes
// the form of another map from the priority to the vote itself so that each priority is
@@ -45,8 +47,9 @@
@GuardedBy("mStorageLock")
private final SparseArray<SparseArray<Vote>> mVotesByDisplay = new SparseArray<>();
- VotesStorage(@NonNull Listener listener) {
+ VotesStorage(@NonNull Listener listener, @Nullable VotesStatsReporter votesStatsReporter) {
mListener = listener;
+ mVotesStatsReporter = votesStatsReporter;
}
/** sets logging enabled/disabled for this class */
void setLoggingEnabled(boolean loggingEnabled) {
@@ -110,17 +113,26 @@
changed = true;
}
}
- Trace.traceCounter(Trace.TRACE_TAG_POWER,
- TAG + "." + displayId + ":" + Vote.priorityToString(priority),
- getMaxPhysicalRefreshRate(vote));
if (mLoggingEnabled) {
Slog.i(TAG, "Updated votes for display=" + displayId + " votes=" + votes);
}
if (changed) {
+ reportVoteStats(displayId, priority, vote);
mListener.onChanged();
}
}
+ private void reportVoteStats(int displayId, int priority, @Nullable Vote vote) {
+ if (mVotesStatsReporter == null) {
+ return;
+ }
+ if (vote == null) {
+ mVotesStatsReporter.reportVoteRemoved(displayId, priority);
+ } else {
+ mVotesStatsReporter.reportVoteAdded(displayId, priority, vote);
+ }
+ }
+
/** dump class values, for debugging */
void dump(@NonNull PrintWriter pw) {
SparseArray<SparseArray<Vote>> votesByDisplayLocal = new SparseArray<>();
@@ -157,21 +169,6 @@
}
}
- private static int getMaxPhysicalRefreshRate(@Nullable Vote vote) {
- if (vote == null) {
- return -1;
- } else if (vote instanceof RefreshRateVote.PhysicalVote physicalVote) {
- return (int) physicalVote.mMaxRefreshRate;
- } else if (vote instanceof CombinedVote combinedVote) {
- return combinedVote.mVotes.stream()
- .filter(v -> v instanceof RefreshRateVote.PhysicalVote)
- .map(pv -> (int) (((RefreshRateVote.PhysicalVote) pv).mMaxRefreshRate))
- .min(Integer::compare)
- .orElse(1000); // for visualisation
- }
- return 1000; // for visualisation, otherwise e.g. -1 -> 60 will be unnoticeable
- }
-
interface Listener {
void onChanged();
}
diff --git a/services/core/java/com/android/server/inputmethod/ClientController.java b/services/core/java/com/android/server/inputmethod/ClientController.java
new file mode 100644
index 0000000..2934640
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/ClientController.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2023 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.inputmethod;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageManagerInternal;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import android.view.inputmethod.InputBinding;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.IInputMethodClient;
+import com.android.internal.inputmethod.IRemoteInputConnection;
+
+/**
+ * Store and manage {@link InputMethodManagerService} clients. This class was designed to be a
+ * singleton in {@link InputMethodManagerService} since it stores information about all clients,
+ * still the current client will be defined per display.
+ *
+ * <p>
+ * As part of the re-architecture plan (described in go/imms-rearchitecture-plan), the following
+ * fields and methods will be moved out from IMMS and placed here:
+ * <ul>
+ * <li>mCurClient (ClientState)</li>
+ * <li>mClients (ArrayMap of ClientState indexed by IBinder)</li>
+ * <li>mLastSwitchUserId</li>
+ * </ul>
+ * <p>
+ * Nested Classes (to move from IMMS):
+ * <ul>
+ * <li>ClientDeathRecipient</li>
+ * <li>ClientState<</li>
+ * </ul>
+ * <p>
+ * Methods to rewrite and/or extract from IMMS and move here:
+ * <ul>
+ * <li>addClient</li>
+ * <li>removeClient</li>
+ * <li>verifyClientAndPackageMatch</li>
+ * <li>setImeTraceEnabledForAllClients (make it reactive)</li>
+ * <li>unbindCurrentClient</li>
+ * </ul>
+ */
+// TODO(b/314150112): Update the Javadoc above, by removing the re-architecture steps, once this
+// class is finalized
+final class ClientController {
+
+ // TODO(b/314150112): Make this field private when breaking the cycle with IMMS.
+ @GuardedBy("ImfLock.class")
+ final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
+
+ private final PackageManagerInternal mPackageManagerInternal;
+
+ ClientController(PackageManagerInternal packageManagerInternal) {
+ mPackageManagerInternal = packageManagerInternal;
+ }
+
+ @GuardedBy("ImfLock.class")
+ void addClient(IInputMethodClientInvoker clientInvoker,
+ IRemoteInputConnection inputConnection,
+ int selfReportedDisplayId, IBinder.DeathRecipient deathRecipient, int callerUid,
+ int callerPid) {
+ // TODO: Optimize this linear search.
+ final int numClients = mClients.size();
+ for (int i = 0; i < numClients; ++i) {
+ final ClientState state = mClients.valueAt(i);
+ if (state.mUid == callerUid && state.mPid == callerPid
+ && state.mSelfReportedDisplayId == selfReportedDisplayId) {
+ throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid
+ + "/displayId=" + selfReportedDisplayId + " is already registered");
+ }
+ }
+ try {
+ clientInvoker.asBinder().linkToDeath(deathRecipient, 0 /* flags */);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ // We cannot fully avoid race conditions where the client UID already lost the access to
+ // the given self-reported display ID, even if the client is not maliciously reporting
+ // a fake display ID. Unconditionally returning SecurityException just because the
+ // client doesn't pass display ID verification can cause many test failures hence not an
+ // option right now. At the same time
+ // context.getSystemService(InputMethodManager.class)
+ // is expected to return a valid non-null instance at any time if we do not choose to
+ // have the client crash. Thus we do not verify the display ID at all here. Instead we
+ // later check the display ID every time the client needs to interact with the specified
+ // display.
+ mClients.put(clientInvoker.asBinder(), new ClientState(clientInvoker, inputConnection,
+ callerUid, callerPid, selfReportedDisplayId, deathRecipient));
+ }
+
+ @GuardedBy("ImfLock.class")
+ boolean verifyClientAndPackageMatch(
+ @NonNull IInputMethodClient client, @NonNull String packageName) {
+ ClientState cs = mClients.get(client.asBinder());
+ if (cs == null) {
+ throw new IllegalArgumentException("unknown client " + client.asBinder());
+ }
+ return InputMethodUtils.checkIfPackageBelongsToUid(
+ mPackageManagerInternal, cs.mUid, packageName);
+ }
+
+ static final class ClientState {
+ final IInputMethodClientInvoker mClient;
+ final IRemoteInputConnection mFallbackInputConnection;
+ final int mUid;
+ final int mPid;
+ final int mSelfReportedDisplayId;
+ final InputBinding mBinding;
+ final IBinder.DeathRecipient mClientDeathRecipient;
+
+ @GuardedBy("ImfLock.class")
+ boolean mSessionRequested;
+
+ @GuardedBy("ImfLock.class")
+ boolean mSessionRequestedForAccessibility;
+
+ @GuardedBy("ImfLock.class")
+ InputMethodManagerService.SessionState mCurSession;
+
+ @GuardedBy("ImfLock.class")
+ SparseArray<InputMethodManagerService.AccessibilitySessionState> mAccessibilitySessions =
+ new SparseArray<>();
+
+ @Override
+ public String toString() {
+ return "ClientState{" + Integer.toHexString(
+ System.identityHashCode(this)) + " mUid=" + mUid
+ + " mPid=" + mPid + " mSelfReportedDisplayId=" + mSelfReportedDisplayId + "}";
+ }
+
+ ClientState(IInputMethodClientInvoker client,
+ IRemoteInputConnection fallbackInputConnection,
+ int uid, int pid, int selfReportedDisplayId,
+ IBinder.DeathRecipient clientDeathRecipient) {
+ mClient = client;
+ mFallbackInputConnection = fallbackInputConnection;
+ mUid = uid;
+ mPid = pid;
+ mSelfReportedDisplayId = selfReportedDisplayId;
+ mBinding = new InputBinding(null /*conn*/, mFallbackInputConnection.asBinder(), mUid,
+ mPid);
+ mClientDeathRecipient = clientDeathRecipient;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java b/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
index f0e4b0f5..898d5a5 100644
--- a/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
+++ b/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
@@ -19,6 +19,8 @@
import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.util.ArrayMap;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
@@ -33,9 +35,26 @@
@GuardedBy("ImfLock.class")
private final ArrayList<InputMethodSubtypeHandle> mSubtypeHandles = new ArrayList<>();
+ @UserIdInt
+ private final int mUserId;
+
+ @AnyThread
+ @UserIdInt
+ int getUserId() {
+ return mUserId;
+ }
+
+ HardwareKeyboardShortcutController(
+ @NonNull ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId) {
+ mUserId = userId;
+ reset(methodMap);
+ }
+
@GuardedBy("ImfLock.class")
- void reset(@NonNull InputMethodUtils.InputMethodSettings settings) {
+ void reset(@NonNull ArrayMap<String, InputMethodInfo> methodMap) {
mSubtypeHandles.clear();
+ final InputMethodUtils.InputMethodSettings settings =
+ new InputMethodUtils.InputMethodSettings(methodMap, mUserId);
for (final InputMethodInfo imi : settings.getEnabledInputMethodListLocked()) {
if (!imi.shouldShowInInputMethodPicker()) {
continue;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 24bcb4e..25ec683 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.inputmethod;
+import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import static android.os.IServiceManager.DUMP_FLAG_PROTO;
@@ -47,6 +48,7 @@
import static android.view.WindowManager.DISPLAY_IME_POLICY_HIDE;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
+import static com.android.server.inputmethod.ClientController.ClientState;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeTargetWindowState;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeVisibilityResult;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME;
@@ -126,7 +128,6 @@
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.Flags;
import android.view.inputmethod.ImeTracker;
-import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceFileProto;
@@ -272,13 +273,15 @@
@NonNull
private final String[] mNonPreemptibleInputMethods;
+ // TODO(b/314150112): Move this to ClientController.
@UserIdInt
private int mLastSwitchUserId;
final Context mContext;
final Resources mRes;
private final Handler mHandler;
- private final InputMethodSettings mSettings;
+ @NonNull
+ private InputMethodSettings mSettings;
final SettingsObserver mSettingsObserver;
private final SparseBooleanArray mLoggedDeniedGetInputMethodWindowVisibleHeightForUid =
new SparseBooleanArray(0);
@@ -315,12 +318,17 @@
// All known input methods.
final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
private final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
+
// Mapping from deviceId to the device-specific imeId for that device.
+ @GuardedBy("ImfLock.class")
private final SparseArray<String> mVirtualDeviceMethodMap = new SparseArray<>();
- private final InputMethodSubtypeSwitchingController mSwitchingController;
- final HardwareKeyboardShortcutController mHardwareKeyboardShortcutController =
- new HardwareKeyboardShortcutController();
+ // TODO: Instantiate mSwitchingController for each user.
+ @NonNull
+ private InputMethodSubtypeSwitchingController mSwitchingController;
+ // TODO: Instantiate mHardwareKeyboardShortcutController for each user.
+ @NonNull
+ private HardwareKeyboardShortcutController mHardwareKeyboardShortcutController;
/**
* Tracks how many times {@link #mMethodMap} was updated.
@@ -339,6 +347,9 @@
@GuardedBy("ImfLock.class")
private int mDisplayIdToShowIme = INVALID_DISPLAY;
+ @GuardedBy("ImfLock.class")
+ private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT;
+
@Nullable private StatusBarManagerInternal mStatusBarManagerInternal;
private boolean mShowOngoingImeSwitcherForPhones;
@GuardedBy("ImfLock.class")
@@ -383,7 +394,7 @@
/**
* Record session state for an accessibility service.
*/
- private static class AccessibilitySessionState {
+ static class AccessibilitySessionState {
final ClientState mClient;
// Id of the accessibility service.
final int mId;
@@ -407,58 +418,10 @@
}
}
- private static final class ClientDeathRecipient implements IBinder.DeathRecipient {
- private final InputMethodManagerService mImms;
- private final IInputMethodClient mClient;
-
- ClientDeathRecipient(InputMethodManagerService imms, IInputMethodClient client) {
- mImms = imms;
- mClient = client;
- }
-
- @Override
- public void binderDied() {
- mImms.removeClient(mClient);
- }
- }
-
- static final class ClientState {
- final IInputMethodClientInvoker mClient;
- final IRemoteInputConnection mFallbackInputConnection;
- final int mUid;
- final int mPid;
- final int mSelfReportedDisplayId;
- final InputBinding mBinding;
- final ClientDeathRecipient mClientDeathRecipient;
-
- boolean mSessionRequested;
- boolean mSessionRequestedForAccessibility;
- SessionState mCurSession;
- SparseArray<AccessibilitySessionState> mAccessibilitySessions = new SparseArray<>();
-
- @Override
- public String toString() {
- return "ClientState{" + Integer.toHexString(
- System.identityHashCode(this)) + " mUid=" + mUid
- + " mPid=" + mPid + " mSelfReportedDisplayId=" + mSelfReportedDisplayId + "}";
- }
-
- ClientState(IInputMethodClientInvoker client,
- IRemoteInputConnection fallbackInputConnection,
- int uid, int pid, int selfReportedDisplayId,
- ClientDeathRecipient clientDeathRecipient) {
- mClient = client;
- mFallbackInputConnection = fallbackInputConnection;
- mUid = uid;
- mPid = pid;
- mSelfReportedDisplayId = selfReportedDisplayId;
- mBinding = new InputBinding(null, mFallbackInputConnection.asBinder(), mUid, mPid);
- mClientDeathRecipient = clientDeathRecipient;
- }
- }
-
- @GuardedBy("ImfLock.class")
- final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
+ /**
+ * Manages the IME clients.
+ */
+ private final ClientController mClientController;
/**
* Set once the system is ready to run third party code.
@@ -516,6 +479,7 @@
/**
* The client that is currently bound to an input method.
*/
+ // TODO(b/314150112): Move this to ClientController.
@Nullable
private ClientState mCurClient;
@@ -856,8 +820,9 @@
@Nullable
final String mImeSurfaceParentName;
- Entry(ClientState client, EditorInfo editorInfo, String focusedWindowName,
- @SoftInputModeFlags int softInputMode, @SoftInputShowHideReason int reason,
+ Entry(ClientState client, EditorInfo editorInfo,
+ String focusedWindowName, @SoftInputModeFlags int softInputMode,
+ @SoftInputShowHideReason int reason,
boolean inFullscreenMode, String requestWindowName,
@Nullable String imeControlTargetName, @Nullable String imeTargetName,
@Nullable String imeSurfaceParentName) {
@@ -1621,7 +1586,7 @@
if (userId != currentUserId) {
return;
}
- mSettings.switchCurrentUser(currentUserId);
+ mSettings = new InputMethodSettings(mMethodMap, userId);
if (mSystemReady) {
// We need to rebuild IMEs.
buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
@@ -1699,8 +1664,10 @@
AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, userId);
mSwitchingController =
- InputMethodSubtypeSwitchingController.createInstanceLocked(mSettings, context);
- mHardwareKeyboardShortcutController.reset(mSettings);
+ InputMethodSubtypeSwitchingController.createInstanceLocked(context, mMethodMap,
+ userId);
+ mHardwareKeyboardShortcutController =
+ new HardwareKeyboardShortcutController(mMethodMap, userId);
mMenuController = new InputMethodMenuController(this);
mBindingController =
bindingControllerForTesting != null
@@ -1710,6 +1677,7 @@
mVisibilityStateComputer = new ImeVisibilityStateComputer(this);
mVisibilityApplier = new DefaultImeVisibilityApplier(this);
+ mClientController = new ClientController(mPackageManagerInternal);
mPreventImeStartupUnlessTextEditor = mRes.getBoolean(
com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor);
@@ -1821,11 +1789,7 @@
// ContentObserver should be registered again when the user is changed
mSettingsObserver.registerContentObserverLocked(newUserId);
- // If the system is not ready or the device is not yed unlocked by the user, then we use
- // copy-on-write settings.
- final boolean useCopyOnWriteSettings =
- !mSystemReady || !mUserManagerInternal.isUserUnlockingOrUnlocked(newUserId);
- mSettings.switchCurrentUser(newUserId);
+ mSettings = new InputMethodSettings(mMethodMap, newUserId);
// Additional subtypes should be reset when the user is changed
AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, newUserId);
final String defaultImiId = mSettings.getSelectedInputMethod();
@@ -1867,7 +1831,8 @@
mLastSwitchUserId = newUserId;
if (mIsInteractive && clientToBeReset != null) {
- final ClientState cs = mClients.get(clientToBeReset.asBinder());
+ final ClientState cs =
+ mClientController.mClients.get(clientToBeReset.asBinder());
if (cs == null) {
// The client is already gone.
return;
@@ -1887,7 +1852,6 @@
if (!mSystemReady) {
mSystemReady = true;
final int currentUserId = mSettings.getCurrentUserId();
- mSettings.switchCurrentUser(currentUserId);
mStatusBarManagerInternal =
LocalServices.getService(StatusBarManagerInternal.class);
hideStatusBarIconLocked();
@@ -2205,43 +2169,22 @@
// actually running.
final int callerUid = Binder.getCallingUid();
final int callerPid = Binder.getCallingPid();
+
+ // TODO(b/314150112): Move the death recipient logic to ClientController when moving
+ // removeClient method.
+ final IBinder.DeathRecipient deathRecipient = () -> removeClient(client);
+ final IInputMethodClientInvoker clientInvoker =
+ IInputMethodClientInvoker.create(client, mHandler);
synchronized (ImfLock.class) {
- // TODO: Optimize this linear search.
- final int numClients = mClients.size();
- for (int i = 0; i < numClients; ++i) {
- final ClientState state = mClients.valueAt(i);
- if (state.mUid == callerUid && state.mPid == callerPid
- && state.mSelfReportedDisplayId == selfReportedDisplayId) {
- throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid
- + "/displayId=" + selfReportedDisplayId + " is already registered.");
- }
- }
- final ClientDeathRecipient deathRecipient = new ClientDeathRecipient(this, client);
- try {
- client.asBinder().linkToDeath(deathRecipient, 0 /* flags */);
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- }
- // We cannot fully avoid race conditions where the client UID already lost the access to
- // the given self-reported display ID, even if the client is not maliciously reporting
- // a fake display ID. Unconditionally returning SecurityException just because the
- // client doesn't pass display ID verification can cause many test failures hence not an
- // option right now. At the same time
- // context.getSystemService(InputMethodManager.class)
- // is expected to return a valid non-null instance at any time if we do not choose to
- // have the client crash. Thus we do not verify the display ID at all here. Instead we
- // later check the display ID every time the client needs to interact with the specified
- // display.
- final IInputMethodClientInvoker clientInvoker =
- IInputMethodClientInvoker.create(client, mHandler);
- mClients.put(client.asBinder(), new ClientState(clientInvoker, inputConnection,
- callerUid, callerPid, selfReportedDisplayId, deathRecipient));
+ mClientController.addClient(clientInvoker, inputConnection, selfReportedDisplayId,
+ deathRecipient, callerUid, callerPid);
}
}
+ // TODO(b/314150112): Move this to ClientController.
void removeClient(IInputMethodClient client) {
synchronized (ImfLock.class) {
- ClientState cs = mClients.remove(client.asBinder());
+ ClientState cs = mClientController.mClients.remove(client.asBinder());
if (cs != null) {
client.asBinder().unlinkToDeath(cs.mClientDeathRecipient, 0 /* flags */);
clearClientSessionLocked(cs);
@@ -2271,6 +2214,7 @@
}
}
+ // TODO(b/314150112): Move this to ClientController.
@GuardedBy("ImfLock.class")
void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) {
if (mCurClient != null) {
@@ -2323,7 +2267,10 @@
}
}
- /** {@code true} when a {@link ClientState} has attached from starting the input connection. */
+ /**
+ * {@code true} when a {@link ClientState} has attached from starting the
+ * input connection.
+ */
@GuardedBy("ImfLock.class")
boolean hasAttachedClient() {
return mCurClient != null;
@@ -2464,11 +2411,7 @@
@StartInputReason int startInputReason,
int unverifiedTargetSdkVersion,
@NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
- // If no method is currently selected, do nothing.
- final String selectedMethodId = getSelectedMethodIdLocked();
- if (selectedMethodId == null) {
- return InputBindResult.NO_IME;
- }
+ String selectedMethodId = getSelectedMethodIdLocked();
if (!mSystemReady) {
// If the system is not yet ready, we shouldn't be running third
@@ -2493,8 +2436,21 @@
return InputBindResult.NOT_IME_TARGET_WINDOW;
}
final int csDisplayId = cs.mSelfReportedDisplayId;
+ final int oldDisplayIdToShowIme = mDisplayIdToShowIme;
mDisplayIdToShowIme = mVisibilityStateComputer.computeImeDisplayId(winState, csDisplayId);
+ // Potentially override the selected input method if the new display belongs to a virtual
+ // device with a custom IME.
+ if (oldDisplayIdToShowIme != mDisplayIdToShowIme) {
+ final String deviceMethodId = computeCurrentDeviceMethodIdLocked(selectedMethodId);
+ if (deviceMethodId == null) {
+ mVisibilityStateComputer.getImePolicy().setImeHiddenByDisplayPolicy(true);
+ } else if (!Objects.equals(deviceMethodId, selectedMethodId)) {
+ setInputMethodLocked(deviceMethodId, NOT_A_SUBTYPE_ID, mDeviceIdToShowIme);
+ selectedMethodId = deviceMethodId;
+ }
+ }
+
if (mVisibilityStateComputer.getImePolicy().isImeHiddenByDisplayPolicy()) {
hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
null /* resultReceiver */,
@@ -2502,6 +2458,11 @@
return InputBindResult.NO_IME;
}
+ // If no method is currently selected, do nothing.
+ if (selectedMethodId == null) {
+ return InputBindResult.NO_IME;
+ }
+
if (mCurClient != cs) {
prepareClientSwitchLocked(cs);
}
@@ -2568,6 +2529,62 @@
return mBindingController.bindCurrentMethod();
}
+ /**
+ * Update the current deviceId and return the relevant imeId for this device.
+ * 1. If the device changes to virtual and its custom IME is not available, then disable IME.
+ * 2. If the device changes to virtual with valid custom IME, then return the custom IME. If
+ * the old device was default, then store the current imeId so it can be restored.
+ * 3. If the device changes to default, restore the default device IME.
+ * 4. Otherwise keep the current imeId.
+ */
+ @GuardedBy("ImfLock.class")
+ private String computeCurrentDeviceMethodIdLocked(String currentMethodId) {
+ if (mVdmInternal == null) {
+ mVdmInternal = LocalServices.getService(VirtualDeviceManagerInternal.class);
+ }
+ if (mVdmInternal == null || !android.companion.virtual.flags.Flags.vdmCustomIme()) {
+ return currentMethodId;
+ }
+
+ final int oldDeviceId = mDeviceIdToShowIme;
+ mDeviceIdToShowIme = mVdmInternal.getDeviceIdForDisplayId(mDisplayIdToShowIme);
+ if (mDeviceIdToShowIme == oldDeviceId) {
+ return currentMethodId;
+ }
+ if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
+ final String defaultDeviceMethodId = mSettings.getSelectedDefaultDeviceInputMethod();
+ if (DEBUG) {
+ Slog.v(TAG, "Restoring default device input method: " + defaultDeviceMethodId);
+ }
+ return defaultDeviceMethodId;
+ }
+
+ final String deviceMethodId =
+ mVirtualDeviceMethodMap.get(mDeviceIdToShowIme, currentMethodId);
+ if (Objects.equals(deviceMethodId, currentMethodId)) {
+ return currentMethodId;
+ } else if (!mMethodMap.containsKey(deviceMethodId)) {
+ if (DEBUG) {
+ Slog.v(TAG, "Disabling IME on virtual device with id " + mDeviceIdToShowIme
+ + " because its custom input method is not available: " + deviceMethodId);
+ }
+ return null;
+ }
+
+ if (oldDeviceId == DEVICE_ID_DEFAULT) {
+ if (DEBUG) {
+ Slog.v(TAG, "Storing default device input method " + currentMethodId);
+ }
+ mSettings.putSelectedDefaultDeviceInputMethod(currentMethodId);
+ }
+ if (DEBUG) {
+ Slog.v(TAG, "Switching current input method from " + currentMethodId
+ + " to device-specific one " + deviceMethodId + " because the current display "
+ + mDisplayIdToShowIme + " belongs to device with id " + mDeviceIdToShowIme);
+ }
+ return deviceMethodId;
+ }
+
@GuardedBy("ImfLock.class")
void invalidateAutofillSessionLocked() {
mAutofillController.invalidateAutofillSession();
@@ -2897,10 +2914,10 @@
@GuardedBy("ImfLock.class")
void clearClientSessionsLocked() {
if (getCurMethodLocked() != null) {
- final int numClients = mClients.size();
+ final int numClients = mClientController.mClients.size();
for (int i = 0; i < numClients; ++i) {
- clearClientSessionLocked(mClients.valueAt(i));
- clearClientSessionForAccessibilityLocked(mClients.valueAt(i));
+ clearClientSessionLocked(mClientController.mClients.valueAt(i));
+ clearClientSessionForAccessibilityLocked(mClientController.mClients.valueAt(i));
}
finishSessionLocked(mEnabledSession);
@@ -3218,13 +3235,21 @@
// There is no longer an input method set, so stop any current one.
resetCurrentMethodAndClientLocked(UnbindReason.NO_IME);
}
- // Here is not the perfect place to reset the switching controller. Ideally
- // mSwitchingController and mSettings should be able to share the same state.
- // TODO: Make sure that mSwitchingController and mSettings are sharing the
- // the same enabled IMEs list.
- mSwitchingController.resetCircularListLocked(mContext);
- mHardwareKeyboardShortcutController.reset(mSettings);
+ // TODO: Instantiate mSwitchingController for each user.
+ if (mSettings.getCurrentUserId() == mSwitchingController.getUserId()) {
+ mSwitchingController.resetCircularListLocked(mMethodMap);
+ } else {
+ mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
+ mContext, mMethodMap, mSettings.getCurrentUserId());
+ }
+ // TODO: Instantiate mHardwareKeyboardShortcutController for each user.
+ if (mSettings.getCurrentUserId() == mHardwareKeyboardShortcutController.getUserId()) {
+ mHardwareKeyboardShortcutController.reset(mMethodMap);
+ } else {
+ mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController(
+ mMethodMap, mSettings.getCurrentUserId());
+ }
sendOnNavButtonFlagsChangedLocked();
}
@@ -3242,6 +3267,11 @@
@GuardedBy("ImfLock.class")
void setInputMethodLocked(String id, int subtypeId) {
+ setInputMethodLocked(id, subtypeId, DEVICE_ID_DEFAULT);
+ }
+
+ @GuardedBy("ImfLock.class")
+ void setInputMethodLocked(String id, int subtypeId, int deviceId) {
InputMethodInfo info = mMethodMap.get(id);
if (info == null) {
throw getExceptionForUnknownImeId(id);
@@ -3285,6 +3315,14 @@
}
// Changing to a different IME.
+ if (mDeviceIdToShowIme != DEVICE_ID_DEFAULT && deviceId == DEVICE_ID_DEFAULT) {
+ // This change should only be applicable to the default device but the current input
+ // method is a custom one specific to a virtual device. So only update the settings
+ // entry used to restore the default device input method once we want to show the IME
+ // back on the default device.
+ mSettings.putSelectedDefaultDeviceInputMethod(id);
+ return;
+ }
IInputMethodInvoker curMethod = getCurMethodLocked();
if (curMethod != null) {
curMethod.removeStylusHandwritingWindow();
@@ -3414,9 +3452,12 @@
+ " pref is disabled for user: " + userId);
return;
}
- if (!verifyClientAndPackageMatch(client, delegatorPackageName)) {
- Slog.w(TAG, "prepareStylusHandwritingDelegation() fail");
- throw new IllegalArgumentException("Delegator doesn't match Uid");
+ synchronized (ImfLock.class) {
+ if (!mClientController.verifyClientAndPackageMatch(client,
+ delegatorPackageName)) {
+ Slog.w(TAG, "prepareStylusHandwritingDelegation() fail");
+ throw new IllegalArgumentException("Delegator doesn't match Uid");
+ }
}
schedulePrepareStylusHandwritingDelegation(
userId, delegatePackageName, delegatorPackageName);
@@ -3442,30 +3483,17 @@
return true;
}
- private boolean verifyClientAndPackageMatch(
- @NonNull IInputMethodClient client, @NonNull String packageName) {
- ClientState cs;
- synchronized (ImfLock.class) {
- cs = mClients.get(client.asBinder());
- }
- if (cs == null) {
- throw new IllegalArgumentException("unknown client " + client.asBinder());
- }
- return InputMethodUtils.checkIfPackageBelongsToUid(
- mPackageManagerInternal, cs.mUid, packageName);
- }
-
private boolean verifyDelegator(
@NonNull IInputMethodClient client,
@NonNull String delegatePackageName,
@NonNull String delegatorPackageName,
@InputMethodManager.HandwritingDelegateFlags int flags) {
- if (!verifyClientAndPackageMatch(client, delegatePackageName)) {
- Slog.w(TAG, "Delegate package does not belong to the same user. Ignoring"
- + " startStylusHandwriting");
- return false;
- }
synchronized (ImfLock.class) {
+ if (!mClientController.verifyClientAndPackageMatch(client, delegatePackageName)) {
+ Slog.w(TAG, "Delegate package does not belong to the same user. Ignoring"
+ + " startStylusHandwriting");
+ return false;
+ }
boolean homeDelegatorAllowed =
(flags & InputMethodManager.HANDWRITING_DELEGATE_FLAG_HOME_DELEGATOR_ALLOWED)
!= 0;
@@ -3728,7 +3756,7 @@
return InputBindResult.INVALID_USER;
}
- final ClientState cs = mClients.get(client.asBinder());
+ final ClientState cs = mClientController.mClients.get(client.asBinder());
if (cs == null) {
throw new IllegalArgumentException("unknown client " + client.asBinder());
}
@@ -3902,7 +3930,8 @@
// We need to check if this is the current client with
// focus in the window manager, to allow this call to
// be made before input is started in it.
- final ClientState cs = mClients.get(client.asBinder());
+ final ClientState cs =
+ mClientController.mClients.get(client.asBinder());
if (cs == null) {
ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_CLIENT_KNOWN);
throw new IllegalArgumentException("unknown client " + client.asBinder());
@@ -4526,7 +4555,7 @@
ImeTracing.getInstance().startTrace(null /* printwriter */);
ArrayMap<IBinder, ClientState> clients;
synchronized (ImfLock.class) {
- clients = new ArrayMap<>(mClients);
+ clients = new ArrayMap<>(mClientController.mClients);
}
for (ClientState state : clients.values()) {
if (state != null) {
@@ -4544,7 +4573,7 @@
ImeTracing.getInstance().stopTrace(null /* printwriter */);
ArrayMap<IBinder, ClientState> clients;
synchronized (ImfLock.class) {
- clients = new ArrayMap<>(mClients);
+ clients = new ArrayMap<>(mClientController.mClients);
}
for (ClientState state : clients.values()) {
if (state != null) {
@@ -4598,6 +4627,9 @@
}
return;
}
+ if (mSettings.getCurrentUserId() != mSwitchingController.getUserId()) {
+ return;
+ }
final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked());
if (imi != null) {
mSwitchingController.onUserActionLocked(imi, mCurrentSubtype);
@@ -4835,9 +4867,10 @@
int lastInputMethodSubtypeId =
mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
- final List<ImeSubtypeListItem> imList = mSwitchingController
- .getSortedInputMethodAndSubtypeListForImeMenuLocked(
- showAuxSubtypes, isScreenLocked);
+ final List<ImeSubtypeListItem> imList = InputMethodSubtypeSwitchingController
+ .getSortedInputMethodAndSubtypeList(
+ showAuxSubtypes, isScreenLocked, false, mContext,
+ mMethodMap, mSettings.getCurrentUserId());
mMenuController.showInputMethodMenuLocked(showAuxSubtypes, displayId,
lastInputMethodId, lastInputMethodSubtypeId, imList);
}
@@ -5222,12 +5255,20 @@
updateDefaultVoiceImeIfNeededLocked();
- // Here is not the perfect place to reset the switching controller. Ideally
- // mSwitchingController and mSettings should be able to share the same state.
- // TODO: Make sure that mSwitchingController and mSettings are sharing the
- // the same enabled IMEs list.
- mSwitchingController.resetCircularListLocked(mContext);
- mHardwareKeyboardShortcutController.reset(mSettings);
+ // TODO: Instantiate mSwitchingController for each user.
+ if (mSettings.getCurrentUserId() == mSwitchingController.getUserId()) {
+ mSwitchingController.resetCircularListLocked(mMethodMap);
+ } else {
+ mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
+ mContext, mMethodMap, mSettings.getCurrentUserId());
+ }
+ // TODO: Instantiate mHardwareKeyboardShortcutController for each user.
+ if (mSettings.getCurrentUserId() == mHardwareKeyboardShortcutController.getUserId()) {
+ mHardwareKeyboardShortcutController.reset(mMethodMap);
+ } else {
+ mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController(
+ mMethodMap, mSettings.getCurrentUserId());
+ }
sendOnNavButtonFlagsChangedLocked();
@@ -5308,11 +5349,21 @@
StringBuilder builder = new StringBuilder();
if (mSettings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
builder, enabledInputMethodsList, id)) {
- // Disabled input method is currently selected, switch to another one.
- final String selId = mSettings.getSelectedInputMethod();
- if (id.equals(selId) && !chooseNewDefaultIMELocked()) {
- Slog.i(TAG, "Can't find new IME, unsetting the current input method.");
- resetSelectedInputMethodAndSubtypeLocked("");
+ if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
+ // Disabled input method is currently selected, switch to another one.
+ final String selId = mSettings.getSelectedInputMethod();
+ if (id.equals(selId) && !chooseNewDefaultIMELocked()) {
+ Slog.i(TAG, "Can't find new IME, unsetting the current input method.");
+ resetSelectedInputMethodAndSubtypeLocked("");
+ }
+ } else if (id.equals(mSettings.getSelectedDefaultDeviceInputMethod())) {
+ // Disabled default device IME while using a virtual device one, choose a
+ // new default one but only update the settings.
+ InputMethodInfo newDefaultIme =
+ InputMethodInfoUtils.getMostApplicableDefaultIME(
+ mSettings.getEnabledInputMethodListLocked());
+ mSettings.putSelectedDefaultDeviceInputMethod(
+ newDefaultIme == null ? "" : newDefaultIme.getId());
}
// Previous state was enabled.
return true;
@@ -5652,9 +5703,8 @@
@Override
public void setVirtualDeviceInputMethodForAllUsers(int deviceId, @Nullable String imeId) {
- // TODO(b/287269288): validate that id belongs to a valid virtual device instead.
- Preconditions.checkArgument(deviceId != Context.DEVICE_ID_DEFAULT,
- "DeviceId " + deviceId + " does not belong to a virtual device.");
+ Preconditions.checkArgument(deviceId != DEVICE_ID_DEFAULT,
+ TextUtils.formatSimple("DeviceId %d is not a virtual device id.", deviceId));
synchronized (ImfLock.class) {
if (imeId == null) {
mVirtualDeviceMethodMap.remove(deviceId);
@@ -5768,10 +5818,10 @@
// We only have sessions when we bound to an input method. Remove this session
// from all clients.
if (getCurMethodLocked() != null) {
- final int numClients = mClients.size();
+ final int numClients = mClientController.mClients.size();
for (int i = 0; i < numClients; ++i) {
- clearClientSessionForAccessibilityLocked(mClients.valueAt(i),
- accessibilityConnectionId);
+ clearClientSessionForAccessibilityLocked(
+ mClientController.mClients.valueAt(i), accessibilityConnectionId);
}
AccessibilitySessionState session = mEnabledAccessibilitySessions.get(
accessibilityConnectionId);
@@ -5956,9 +6006,10 @@
info.dump(p, " ");
}
p.println(" ClientStates:");
- final int numClients = mClients.size();
+ // TODO(b/314150112): move client related dump info to ClientController#dump
+ final int numClients = mClientController.mClients.size();
for (int i = 0; i < numClients; ++i) {
- final ClientState ci = mClients.valueAt(i);
+ final ClientState ci = mClientController.mClients.valueAt(i);
p.println(" " + ci + ":");
p.println(" client=" + ci.mClient);
p.println(" fallbackInputConnection=" + ci.mFallbackInputConnection);
@@ -6577,7 +6628,7 @@
boolean isImeTraceEnabled = ImeTracing.getInstance().isEnabled();
ArrayMap<IBinder, ClientState> clients;
synchronized (ImfLock.class) {
- clients = new ArrayMap<>(mClients);
+ clients = new ArrayMap<>(mClientController.mClients);
}
for (ClientState state : clients.values()) {
if (state != null) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index 431aabd..4439b06 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -16,10 +16,14 @@
package com.android.server.inputmethod;
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.content.Context;
-import android.content.pm.PackageManager;
+import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Printer;
import android.util.Slog;
@@ -32,7 +36,6 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.Locale;
import java.util.Objects;
/**
@@ -154,79 +157,71 @@
}
}
- private static class InputMethodAndSubtypeList {
- private final Context mContext;
- // Used to load label
- private final PackageManager mPm;
- private final String mSystemLocaleStr;
- private final InputMethodSettings mSettings;
+ static List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(
+ boolean includeAuxiliarySubtypes, boolean isScreenLocked, boolean forImeMenu,
+ @NonNull Context context, @NonNull ArrayMap<String, InputMethodInfo> methodMap,
+ @UserIdInt int userId) {
+ final Context userAwareContext = context.getUserId() == userId
+ ? context
+ : context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
+ final String mSystemLocaleStr = SystemLocaleWrapper.get(userId).get(0).toLanguageTag();
+ final InputMethodSettings settings = new InputMethodSettings(methodMap, userId);
- InputMethodAndSubtypeList(Context context, InputMethodSettings settings) {
- mContext = context;
- mSettings = settings;
- mPm = context.getPackageManager();
- final Locale locale = context.getResources().getConfiguration().locale;
- mSystemLocaleStr = locale != null ? locale.toString() : "";
+ final ArrayList<InputMethodInfo> imis = settings.getEnabledInputMethodListLocked();
+ if (imis.isEmpty()) {
+ return Collections.emptyList();
}
-
- public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(
- boolean includeAuxiliarySubtypes, boolean isScreenLocked, boolean forImeMenu) {
- final ArrayList<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
- if (imis.isEmpty()) {
- return Collections.emptyList();
+ if (isScreenLocked && includeAuxiliarySubtypes) {
+ if (DEBUG) {
+ Slog.w(TAG, "Auxiliary subtypes are not allowed to be shown in lock screen.");
}
- if (isScreenLocked && includeAuxiliarySubtypes) {
+ includeAuxiliarySubtypes = false;
+ }
+ final ArrayList<ImeSubtypeListItem> imList = new ArrayList<>();
+ final int numImes = imis.size();
+ for (int i = 0; i < numImes; ++i) {
+ final InputMethodInfo imi = imis.get(i);
+ if (forImeMenu && !imi.shouldShowInInputMethodPicker()) {
+ continue;
+ }
+ final List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList =
+ settings.getEnabledInputMethodSubtypeListLocked(imi, true);
+ final ArraySet<String> enabledSubtypeSet = new ArraySet<>();
+ for (InputMethodSubtype subtype : explicitlyOrImplicitlyEnabledSubtypeList) {
+ enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
+ }
+ final CharSequence imeLabel = imi.loadLabel(userAwareContext.getPackageManager());
+ if (enabledSubtypeSet.size() > 0) {
+ final int subtypeCount = imi.getSubtypeCount();
if (DEBUG) {
- Slog.w(TAG, "Auxiliary subtypes are not allowed to be shown in lock screen.");
+ Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
}
- includeAuxiliarySubtypes = false;
- }
- final ArrayList<ImeSubtypeListItem> imList = new ArrayList<>();
- final int numImes = imis.size();
- for (int i = 0; i < numImes; ++i) {
- final InputMethodInfo imi = imis.get(i);
- if (forImeMenu && !imi.shouldShowInInputMethodPicker()) {
- continue;
- }
- final List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList =
- mSettings.getEnabledInputMethodSubtypeListLocked(imi, true);
- final ArraySet<String> enabledSubtypeSet = new ArraySet<>();
- for (InputMethodSubtype subtype : explicitlyOrImplicitlyEnabledSubtypeList) {
- enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
- }
- final CharSequence imeLabel = imi.loadLabel(mPm);
- if (enabledSubtypeSet.size() > 0) {
- final int subtypeCount = imi.getSubtypeCount();
- if (DEBUG) {
- Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
- }
- for (int j = 0; j < subtypeCount; ++j) {
- final InputMethodSubtype subtype = imi.getSubtypeAt(j);
- final String subtypeHashCode = String.valueOf(subtype.hashCode());
- // We show all enabled IMEs and subtypes when an IME is shown.
- if (enabledSubtypeSet.contains(subtypeHashCode)
- && (includeAuxiliarySubtypes || !subtype.isAuxiliary())) {
- final CharSequence subtypeLabel =
- subtype.overridesImplicitlyEnabledSubtype() ? null : subtype
- .getDisplayName(mContext, imi.getPackageName(),
- imi.getServiceInfo().applicationInfo);
- imList.add(new ImeSubtypeListItem(imeLabel,
- subtypeLabel, imi, j, subtype.getLocale(), mSystemLocaleStr));
+ for (int j = 0; j < subtypeCount; ++j) {
+ final InputMethodSubtype subtype = imi.getSubtypeAt(j);
+ final String subtypeHashCode = String.valueOf(subtype.hashCode());
+ // We show all enabled IMEs and subtypes when an IME is shown.
+ if (enabledSubtypeSet.contains(subtypeHashCode)
+ && (includeAuxiliarySubtypes || !subtype.isAuxiliary())) {
+ final CharSequence subtypeLabel =
+ subtype.overridesImplicitlyEnabledSubtype() ? null : subtype
+ .getDisplayName(userAwareContext, imi.getPackageName(),
+ imi.getServiceInfo().applicationInfo);
+ imList.add(new ImeSubtypeListItem(imeLabel,
+ subtypeLabel, imi, j, subtype.getLocale(), mSystemLocaleStr));
- // Removing this subtype from enabledSubtypeSet because we no
- // longer need to add an entry of this subtype to imList to avoid
- // duplicated entries.
- enabledSubtypeSet.remove(subtypeHashCode);
- }
+ // Removing this subtype from enabledSubtypeSet because we no
+ // longer need to add an entry of this subtype to imList to avoid
+ // duplicated entries.
+ enabledSubtypeSet.remove(subtypeHashCode);
}
- } else {
- imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID, null,
- mSystemLocaleStr));
}
+ } else {
+ imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID, null,
+ mSystemLocaleStr));
}
- Collections.sort(imList);
- return imList;
}
+ Collections.sort(imList);
+ return imList;
}
private static int calculateSubtypeId(InputMethodInfo imi, InputMethodSubtype subtype) {
@@ -479,18 +474,32 @@
}
}
- private final InputMethodSettings mSettings;
- private InputMethodAndSubtypeList mSubtypeList;
+ private final Context mContext;
+ @UserIdInt
+ private final int mUserId;
private ControllerImpl mController;
- private InputMethodSubtypeSwitchingController(InputMethodSettings settings, Context context) {
- mSettings = settings;
- resetCircularListLocked(context);
+ private InputMethodSubtypeSwitchingController(@NonNull Context context,
+ @NonNull ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId) {
+ mContext = context;
+ mUserId = userId;
+ mController = ControllerImpl.createFrom(null,
+ getSortedInputMethodAndSubtypeList(
+ false /* includeAuxiliarySubtypes */, false /* isScreenLocked */,
+ false /* forImeMenu */, context, methodMap, userId));
}
+ @NonNull
public static InputMethodSubtypeSwitchingController createInstanceLocked(
- InputMethodSettings settings, Context context) {
- return new InputMethodSubtypeSwitchingController(settings, context);
+ @NonNull Context context,
+ @NonNull ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId) {
+ return new InputMethodSubtypeSwitchingController(context, methodMap, userId);
+ }
+
+ @AnyThread
+ @UserIdInt
+ int getUserId() {
+ return mUserId;
}
public void onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype) {
@@ -503,12 +512,12 @@
mController.onUserActionLocked(imi, subtype);
}
- public void resetCircularListLocked(Context context) {
- mSubtypeList = new InputMethodAndSubtypeList(context, mSettings);
+ public void resetCircularListLocked(
+ @NonNull ArrayMap<String, InputMethodInfo> methodMap) {
mController = ControllerImpl.createFrom(mController,
- mSubtypeList.getSortedInputMethodAndSubtypeList(
+ getSortedInputMethodAndSubtypeList(
false /* includeAuxiliarySubtypes */, false /* isScreenLocked */,
- false /* forImeMenu */));
+ false /* forImeMenu */, mContext, methodMap, mUserId));
}
public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi,
@@ -522,12 +531,6 @@
return mController.getNextInputMethod(onlyCurrentIme, imi, subtype);
}
- public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListForImeMenuLocked(
- boolean includingAuxiliarySubtypes, boolean isScreenLocked) {
- return mSubtypeList.getSortedInputMethodAndSubtypeList(
- includingAuxiliarySubtypes, isScreenLocked, true /* forImeMenu */);
- }
-
public void dump(final Printer pw) {
if (mController != null) {
mController.dump(pw);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index a0b55ed..fb57c09 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -51,6 +51,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.StringJoiner;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -214,7 +215,7 @@
private final ArrayMap<String, InputMethodInfo> mMethodMap;
@UserIdInt
- private int mCurrentUserId;
+ private final int mCurrentUserId;
private static void buildEnabledInputMethodsSettingString(
StringBuilder builder, Pair<String, ArrayList<String>> ime) {
@@ -228,19 +229,13 @@
InputMethodSettings(ArrayMap<String, InputMethodInfo> methodMap, @UserIdInt int userId) {
mMethodMap = methodMap;
- switchCurrentUser(userId);
- }
-
- /**
- * Must be called when the current user is changed.
- *
- * @param userId The user ID.
- */
- void switchCurrentUser(@UserIdInt int userId) {
- if (DEBUG) {
- Slog.d(TAG, "--- Switch the current user from " + mCurrentUserId + " to " + userId);
- }
mCurrentUserId = userId;
+ String ime = getSelectedInputMethod();
+ String defaultDeviceIme = getSelectedDefaultDeviceInputMethod();
+ if (defaultDeviceIme != null && !Objects.equals(ime, defaultDeviceIme)) {
+ putSelectedInputMethod(defaultDeviceIme);
+ putSelectedDefaultDeviceInputMethod(null);
+ }
}
private void putString(@NonNull String key, @Nullable String str) {
@@ -636,6 +631,24 @@
return imi;
}
+ @Nullable
+ String getSelectedDefaultDeviceInputMethod() {
+ final String imi = getString(Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null);
+ if (DEBUG) {
+ Slog.d(TAG, "getSelectedDefaultDeviceInputMethodStr: " + imi + ", "
+ + mCurrentUserId);
+ }
+ return imi;
+ }
+
+ void putSelectedDefaultDeviceInputMethod(String imeId) {
+ if (DEBUG) {
+ Slog.d(TAG, "putSelectedDefaultDeviceInputMethodStr: " + imeId + ", "
+ + mCurrentUserId);
+ }
+ putString(Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, imeId);
+ }
+
void putDefaultVoiceInputMethod(String imeId) {
if (DEBUG) {
Slog.d(TAG, "putDefaultVoiceInputMethodStr: " + imeId + ", " + mCurrentUserId);
diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java
index 41d0176..22951d5 100644
--- a/services/core/java/com/android/server/pm/InitAppsHelper.java
+++ b/services/core/java/com/android/server/pm/InitAppsHelper.java
@@ -180,7 +180,9 @@
// priority of system overlays.
final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>();
for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) {
- for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) {
+ final String apexPackageName = mApexManager.getActivePackageNameForApexModuleName(
+ apexInfo.apexModuleName);
+ for (String packageName : mApexManager.getApksInApex(apexPackageName)) {
apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index dcfc855d..376b061 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -25,6 +25,7 @@
import static android.content.pm.ArchivedActivityInfo.bytesFromBitmap;
import static android.content.pm.ArchivedActivityInfo.drawableToBitmap;
import static android.content.pm.PackageInstaller.EXTRA_UNARCHIVE_STATUS;
+import static android.content.pm.PackageInstaller.STATUS_PENDING_USER_ACTION;
import static android.content.pm.PackageInstaller.UNARCHIVAL_OK;
import static android.content.pm.PackageManager.DELETE_ARCHIVE;
import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
@@ -72,10 +73,12 @@
import android.os.IBinder;
import android.os.ParcelableException;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SELinux;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ExceptionUtils;
+import android.util.Pair;
import android.util.Slog;
import com.android.internal.R;
@@ -93,7 +96,9 @@
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
@@ -140,15 +145,23 @@
private final Context mContext;
private final PackageManagerService mPm;
+ private final AppStateHelper mAppStateHelper;
+
@Nullable
private LauncherApps mLauncherApps;
@Nullable
private AppOpsManager mAppOpsManager;
+ /* IntentSender store that maps key: {userId, appPackageName} to respective existing attached
+ unarchival intent sender. */
+ private final Map<Pair<Integer, String>, IntentSender> mLauncherIntentSenders;
+
PackageArchiver(Context context, PackageManagerService mPm) {
this.mContext = context;
this.mPm = mPm;
+ this.mAppStateHelper = new AppStateHelper(mContext);
+ this.mLauncherIntentSenders = new HashMap<>();
}
/** Returns whether a package is archived for a user. */
@@ -235,37 +248,32 @@
// Return early as the calling UID does not match caller package's UID.
return START_CLASS_NOT_FOUND;
}
+
String currentLauncherPackageName = getCurrentLauncherPackageName(userId);
if ((currentLauncherPackageName == null || !callerPackageName.equals(
currentLauncherPackageName)) && callingUid != Process.SHELL_UID) {
// TODO(b/311619990): Remove dependency on SHELL_UID for testing
Slog.e(TAG, TextUtils.formatSimple(
- "callerPackageName: %s does not qualify for archival of package: " + "%s!",
+ "callerPackageName: %s does not qualify for unarchival of package: " + "%s!",
callerPackageName, packageName));
return START_PERMISSION_DENIED;
}
- // TODO(b/302114464): Handle edge cases & also divert to a dialog based on
- // permissions + compat options
- Slog.i(TAG, TextUtils.formatSimple("Unarchival is starting for: %s", packageName));
- try {
- final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
- @Override
- public void send(int code, Intent intent, String resolvedType,
- IBinder allowlistToken,
- IIntentReceiver finishedReceiver, String requiredPermission,
- Bundle options) {
- // TODO(b/302114464): Handle intent sender status codes
- }
- };
+ Slog.i(TAG, TextUtils.formatSimple("Unarchival is starting for: %s", packageName));
+
+ try {
+ // TODO(b/311709794) Make showUnarchivalConfirmation dependent on the compat options.
requestUnarchive(packageName, callerPackageName,
- new IntentSender((IIntentSender) mLocalSender), UserHandle.of(userId));
+ getOrCreateUnarchiveIntentSender(userId, packageName),
+ UserHandle.of(userId),
+ false /* showUnarchivalConfirmation= */);
} catch (Throwable t) {
Slog.e(TAG, TextUtils.formatSimple(
"Unexpected error occurred while unarchiving package %s: %s.", packageName,
t.getLocalizedMessage()));
return START_ABORTED;
}
+
return START_SUCCESS;
}
@@ -321,6 +329,20 @@
return true;
}
+ private IntentSender getOrCreateUnarchiveIntentSender(int userId, String packageName) {
+ Pair<Integer, String> key = Pair.create(userId, packageName);
+ synchronized (mLauncherIntentSenders) {
+ IntentSender intentSender = mLauncherIntentSenders.get(key);
+ if (intentSender != null) {
+ return intentSender;
+ }
+ IntentSender unarchiveIntentSender = new IntentSender(
+ (IIntentSender) new UnarchiveIntentSender());
+ mLauncherIntentSenders.put(key, unarchiveIntentSender);
+ return unarchiveIntentSender;
+ }
+ }
+
/** Creates archived state for the package and user. */
private CompletableFuture<ArchiveState> createArchiveState(String packageName, int userId)
throws PackageManager.NameNotFoundException {
@@ -553,6 +575,15 @@
@NonNull String callerPackageName,
@NonNull IntentSender statusReceiver,
@NonNull UserHandle userHandle) {
+ requestUnarchive(packageName, callerPackageName, statusReceiver, userHandle,
+ false /* showUnarchivalConfirmation= */);
+ }
+
+ private void requestUnarchive(
+ @NonNull String packageName,
+ @NonNull String callerPackageName,
+ @NonNull IntentSender statusReceiver,
+ @NonNull UserHandle userHandle, boolean showUnarchivalConfirmation) {
Objects.requireNonNull(packageName);
Objects.requireNonNull(callerPackageName);
Objects.requireNonNull(statusReceiver);
@@ -597,8 +628,8 @@
+ "an unarchival.");
}
- if (!hasInstallPackages) {
- requestUnarchiveConfirmation(packageName, statusReceiver);
+ if (!hasInstallPackages || showUnarchivalConfirmation) {
+ requestUnarchiveConfirmation(packageName, statusReceiver, userHandle);
return;
}
@@ -622,7 +653,8 @@
() -> unarchiveInternal(packageName, userHandle, installerPackage, draftSessionId));
}
- private void requestUnarchiveConfirmation(String packageName, IntentSender statusReceiver) {
+ private void requestUnarchiveConfirmation(String packageName, IntentSender statusReceiver,
+ UserHandle user) {
final Intent dialogIntent = new Intent(ACTION_UNARCHIVE_DIALOG);
dialogIntent.putExtra(EXTRA_UNARCHIVE_INTENT_SENDER, statusReceiver);
dialogIntent.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, packageName);
@@ -632,6 +664,7 @@
broadcastIntent.putExtra(EXTRA_UNARCHIVE_STATUS,
PackageInstaller.STATUS_PENDING_USER_ACTION);
broadcastIntent.putExtra(Intent.EXTRA_INTENT, dialogIntent);
+ broadcastIntent.putExtra(Intent.EXTRA_USER, user);
sendIntent(statusReceiver, packageName, /* message= */ "", broadcastIntent);
}
@@ -656,6 +689,7 @@
int installerUid = mPm.snapshotComputer().getPackageUid(installerPackage, 0, userId);
// Handles case of repeated unarchival calls for the same package.
+ // TODO(b/316881759) Allow attaching multiple intentSenders to one session.
int existingSessionId = mPm.mInstallerService.getExistingDraftSessionId(installerUid,
sessionParams,
userId);
@@ -849,7 +883,13 @@
void notifyUnarchivalListener(int status, String installerPackageName, String appPackageName,
long requiredStorageBytes, @Nullable PendingIntent userActionIntent,
- IntentSender unarchiveIntentSender, int userId) {
+ @Nullable IntentSender unarchiveIntentSender, int userId) {
+ if (unarchiveIntentSender == null) {
+ // Maybe this can happen if the installer calls reportUnarchivalStatus twice in quick
+ // succession.
+ return;
+ }
+
final Intent broadcastIntent = new Intent();
broadcastIntent.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, appPackageName);
broadcastIntent.putExtra(EXTRA_UNARCHIVE_STATUS, status);
@@ -863,6 +903,7 @@
return;
}
broadcastIntent.putExtra(Intent.EXTRA_INTENT, dialogIntent);
+ broadcastIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
}
final BroadcastOptions options = BroadcastOptions.makeBasic();
@@ -874,6 +915,10 @@
options.toBundle());
} catch (IntentSender.SendIntentException e) {
Slog.e(TAG, TextUtils.formatSimple("Failed to send unarchive intent"), e);
+ } finally {
+ synchronized (mLauncherIntentSenders) {
+ mLauncherIntentSenders.remove(Pair.create(userId, appPackageName));
+ }
}
}
@@ -883,6 +928,7 @@
long requiredStorageBytes, PendingIntent userActionIntent, int userId) {
final Intent dialogIntent = new Intent(ACTION_UNARCHIVE_ERROR_DIALOG);
dialogIntent.putExtra(EXTRA_UNARCHIVE_STATUS, status);
+ dialogIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
if (requiredStorageBytes > 0) {
dialogIntent.putExtra(EXTRA_REQUIRED_BYTES, requiredStorageBytes);
}
@@ -1118,4 +1164,25 @@
return activities.toArray(new ArchivedActivityParcel[activities.size()]);
}
+
+ private class UnarchiveIntentSender extends IIntentSender.Stub {
+ @Override
+ public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+ IIntentReceiver finishedReceiver, String requiredPermission, Bundle options)
+ throws RemoteException {
+ int status = intent.getExtras().getInt(PackageInstaller.EXTRA_UNARCHIVE_STATUS,
+ STATUS_PENDING_USER_ACTION);
+ if (status == UNARCHIVAL_OK) {
+ return;
+ }
+ Intent extraIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
+ UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class);
+ if (extraIntent != null && user != null
+ && mAppStateHelper.isAppTopVisible(
+ getCurrentLauncherPackageName(user.getIdentifier()))) {
+ extraIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivityAsUser(extraIntent, user);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index cbd65a4..0a23dfb 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -291,9 +291,7 @@
@NonNull
private final RequestThrottle mSettingsWriteRequest = new RequestThrottle(IoThread.getHandler(),
() -> {
- synchronized (mSessions) {
- return writeSessionsLocked();
- }
+ return writeSessions();
});
public PackageInstallerService(Context context, PackageManagerService pm,
@@ -600,9 +598,16 @@
mHistoricalSessionsByInstaller.get(installerUid) + 1);
}
- @GuardedBy("mSessions")
- private boolean writeSessionsLocked() {
- if (LOGD) Slog.v(TAG, "writeSessionsLocked()");
+ private boolean writeSessions() {
+ if (LOGD) Slog.v(TAG, "writeSessions()");
+ final PackageInstallerSession[] sessions;
+ synchronized (mSessions) {
+ final int size = mSessions.size();
+ sessions = new PackageInstallerSession[size];
+ for (int i = 0; i < size; i++) {
+ sessions[i] = mSessions.valueAt(i);
+ }
+ }
FileOutputStream fos = null;
try {
@@ -611,9 +616,7 @@
final TypedXmlSerializer out = Xml.resolveSerializer(fos);
out.startDocument(null, true);
out.startTag(null, TAG_SESSIONS);
- final int size = mSessions.size();
- for (int i = 0; i < size; i++) {
- final PackageInstallerSession session = mSessions.valueAt(i);
+ for (var session : sessions) {
session.write(out, mSessionsDir);
}
out.endTag(null, TAG_SESSIONS);
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 02e28dd..7bd6a43 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -487,6 +487,8 @@
synchronized (mPm.mInstallLock) {
cleanUpResourcesLI(codeFile, instructionSets);
}
+ // TODO: open logging to help debug, will delete or add debug flag
+ Slog.d(TAG, "cleanUpResources for " + codeFile);
if (packageName == null) {
return;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index edce3ec..f651dbf 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4211,6 +4211,14 @@
return redoLayout;
}
+ /**
+ * Shows the keyguard without immediately locking the device.
+ */
+ @Override
+ public void showDismissibleKeyguard() {
+ mKeyguardDelegate.showDismissibleKeyguard();
+ }
+
// There are several different flavors of "assistant" that can be launched from
// various parts of the UI.
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 3016b39..2174fd6 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -178,6 +178,12 @@
int applyKeyguardOcclusionChange();
/**
+ * Shows the keyguard immediately if not already shown.
+ * Does NOT immediately request the device to lock.
+ */
+ void showDismissibleKeyguard();
+
+ /**
* Interface to the Window Manager state associated with a particular
* window. You can hold on to an instance of this interface from the call
* to prepareAddWindow() until removeWindow().
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 495e239..d0b70c3 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -410,6 +410,15 @@
}
}
+ /**
+ * Request to show the keyguard immediately without immediately locking the device.
+ */
+ public void showDismissibleKeyguard() {
+ if (mKeyguardService != null) {
+ mKeyguardService.showDismissibleKeyguard();
+ }
+ }
+
public void setCurrentUser(int newUserId) {
if (mKeyguardService != null) {
mKeyguardService.setCurrentUser(newUserId);
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
index 774e261..cd789ea 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
@@ -209,6 +209,16 @@
}
}
+ // Binder interface
+ @Override
+ public void showDismissibleKeyguard() {
+ try {
+ mService.showDismissibleKeyguard();
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
@Override // Binder interface
public void setSwitchingUser(boolean switching) {
try {
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 3ecc985..652cf18 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -411,6 +411,11 @@
mWakeLockLog.onWakeLockReleased(tag, ownerUid);
}
+ /** Shows the keyguard without requesting the device to immediately lock. */
+ public void showDismissibleKeyguard() {
+ mPolicy.showDismissibleKeyguard();
+ }
+
private int getBatteryStatsWakeLockMonitorType(int flags) {
switch (flags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
case PowerManager.PARTIAL_WAKE_LOCK:
@@ -958,7 +963,8 @@
final boolean vibrate = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0;
if (vibrate) {
- mVibrator.vibrate(CHARGING_VIBRATION_EFFECT,
+ mVibrator.vibrate(Process.SYSTEM_UID, mContext.getOpPackageName(),
+ CHARGING_VIBRATION_EFFECT, /* reason= */ "Charging started",
HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index ec5172f..2128c991 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -115,6 +115,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.display.BrightnessSynchronizer;
+import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -308,6 +309,7 @@
private final Context mContext;
private final ServiceThread mHandlerThread;
private final Handler mHandler;
+ private final FoldGracePeriodProvider mFoldGracePeriodProvider;
private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
@Nullable
private final BatterySaverStateMachine mBatterySaverStateMachine;
@@ -1007,6 +1009,10 @@
return new InattentiveSleepWarningController();
}
+ FoldGracePeriodProvider createFoldGracePeriodProvider() {
+ return new FoldGracePeriodProvider();
+ }
+
public SystemPropertiesWrapper createSystemPropertiesWrapper() {
return new SystemPropertiesWrapper() {
@Override
@@ -1147,6 +1153,7 @@
mHandler = injector.createHandler(mHandlerThread.getLooper(),
new PowerManagerHandlerCallback());
mConstants = new Constants(mHandler);
+ mFoldGracePeriodProvider = injector.createFoldGracePeriodProvider();
mAmbientDisplayConfiguration = mInjector.createAmbientDisplayConfiguration(context);
mAmbientDisplaySuppressionController =
mInjector.createAmbientDisplaySuppressionController(
@@ -6933,8 +6940,15 @@
+ ") doesn't exist");
}
if ((flags & PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP) != 0) {
- if (powerGroup.hasWakeLockKeepingScreenOnLocked()) {
- continue;
+ if (mFoldGracePeriodProvider.isEnabled()) {
+ if (!powerGroup.hasWakeLockKeepingScreenOnLocked()) {
+ mNotifier.showDismissibleKeyguard();
+ }
+ continue; // never actually goes to sleep for SOFT_SLEEP
+ } else {
+ if (powerGroup.hasWakeLockKeepingScreenOnLocked()) {
+ continue;
+ }
}
}
if (isNoDoze) {
diff --git a/services/core/java/com/android/server/trust/TEST_MAPPING b/services/core/java/com/android/server/trust/TEST_MAPPING
index fa46acd..0de7c28 100644
--- a/services/core/java/com/android/server/trust/TEST_MAPPING
+++ b/services/core/java/com/android/server/trust/TEST_MAPPING
@@ -12,6 +12,19 @@
]
}
],
+ "postsubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.trust"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
"trust-tablet": [
{
"name": "TrustTests",
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 9a85c42..5c603c2 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -74,6 +74,7 @@
import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.DumpUtils;
@@ -1758,6 +1759,11 @@
}
};
+ @VisibleForTesting
+ void waitForIdle() {
+ mHandler.runWithScissors(() -> {}, 0);
+ }
+
private boolean isTrustUsuallyManagedInternal(int userId) {
synchronized (mTrustUsuallyManagedForUser) {
int i = mTrustUsuallyManagedForUser.indexOfKey(userId);
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 1577cef..2d584c4 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -98,6 +98,8 @@
import android.view.animation.Interpolator;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.TraceBuffer;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -297,6 +299,18 @@
}
}
+ /** It is only used by unit test. */
+ @VisibleForTesting
+ Surface forceShowMagnifierSurface(int displayId) {
+ final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+ if (displayMagnifier != null) {
+ displayMagnifier.mMagnifedViewport.mWindow.setAlpha(DisplayMagnifier.MagnifiedViewport
+ .ViewportWindow.AnimationController.MAX_ALPHA);
+ return displayMagnifier.mMagnifedViewport.mWindow.mSurface;
+ }
+ return null;
+ }
+
void onWindowLayersChanged(int displayId) {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
| FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
@@ -448,6 +462,7 @@
}
}
+ // TODO(b/318327737): Remove parameter 't' when removing flag DRAW_IN_WM_LOCK.
void drawMagnifiedRegionBorderIfNeeded(int displayId, SurfaceControl.Transaction t) {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
mAccessibilityTracing.logTrace(
@@ -1106,11 +1121,19 @@
}
void setMagnifiedRegionBorderShown(boolean shown, boolean animate) {
- if (shown) {
+ if (ViewportWindow.DRAW_IN_WM_LOCK) {
+ if (shown) {
+ mFullRedrawNeeded = true;
+ mOldMagnificationRegion.set(0, 0, 0, 0);
+ }
+ mWindow.setShown(shown, animate);
+ return;
+ }
+ if (mWindow.setShown(shown, animate)) {
mFullRedrawNeeded = true;
+ // Clear the old region, so recomputeBounds will refresh the current region.
mOldMagnificationRegion.set(0, 0, 0, 0);
}
- mWindow.setShown(shown, animate);
}
void getMagnifiedFrameInContentCoords(Rect rect) {
@@ -1130,7 +1153,11 @@
void drawWindowIfNeeded(SurfaceControl.Transaction t) {
recomputeBounds();
- mWindow.drawIfNeeded(t);
+ if (ViewportWindow.DRAW_IN_WM_LOCK) {
+ mWindow.drawOrRemoveIfNeeded(t);
+ return;
+ }
+ mWindow.postDrawIfNeeded();
}
void destroyWindow() {
@@ -1158,23 +1185,28 @@
mWindow.dump(pw, prefix);
}
- private final class ViewportWindow {
+ private final class ViewportWindow implements Runnable {
private static final String SURFACE_TITLE = "Magnification Overlay";
+ // TODO(b/318327737): Remove if it is stable.
+ static final boolean DRAW_IN_WM_LOCK = !Flags.drawMagnifierBorderOutsideWmlock();
private final Region mBounds = new Region();
private final Rect mDirtyRect = new Rect();
private final Paint mPaint = new Paint();
private final SurfaceControl mSurfaceControl;
+ /** After initialization, it should only be accessed from animation thread. */
+ private final SurfaceControl.Transaction mTransaction;
private final BLASTBufferQueue mBlastBufferQueue;
private final Surface mSurface;
private final AnimationController mAnimationController;
private boolean mShown;
+ private boolean mLastSurfaceShown;
private int mAlpha;
- private boolean mInvalidated;
+ private volatile boolean mInvalidated;
ViewportWindow(Context context) {
SurfaceControl surfaceControl = null;
@@ -1202,6 +1234,7 @@
InputMonitor.setTrustedOverlayInputInfo(mSurfaceControl, t,
mDisplayContent.getDisplayId(), "Magnification Overlay");
t.apply();
+ mTransaction = t;
mSurface = mBlastBufferQueue.createSurface();
mAnimationController = new AnimationController(context,
@@ -1219,10 +1252,11 @@
mInvalidated = true;
}
- void setShown(boolean shown, boolean animate) {
+ /** Returns {@code true} if the state is changed to shown. */
+ boolean setShown(boolean shown, boolean animate) {
synchronized (mService.mGlobalLock) {
if (mShown == shown) {
- return;
+ return false;
}
mShown = shown;
mAnimationController.onFrameShownStateChanged(shown, animate);
@@ -1230,6 +1264,7 @@
Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
}
}
+ return shown;
}
@SuppressWarnings("unused")
@@ -1285,7 +1320,22 @@
mService.scheduleAnimationLocked();
}
- void drawIfNeeded(SurfaceControl.Transaction t) {
+ void postDrawIfNeeded() {
+ if (mInvalidated) {
+ mService.mAnimationHandler.post(this);
+ }
+ }
+
+ @Override
+ public void run() {
+ drawOrRemoveIfNeeded(mTransaction);
+ }
+
+ /**
+ * This method must only be called by animation handler directly to make sure
+ * thread safe and there is no lock held outside.
+ */
+ private void drawOrRemoveIfNeeded(SurfaceControl.Transaction t) {
// Drawing variables (alpha, dirty rect, and bounds) access is synchronized
// using WindowManagerGlobalLock. Grab copies of these values before
// drawing on the canvas so that drawing can be performed outside of the lock.
@@ -1293,6 +1343,14 @@
Rect drawingRect = null;
Region drawingBounds = null;
synchronized (mService.mGlobalLock) {
+ if (!DRAW_IN_WM_LOCK && mBlastBufferQueue.mNativeObject == 0) {
+ // Complete removal since releaseSurface has been called.
+ if (mSurface.isValid()) {
+ mTransaction.remove(mSurfaceControl).apply();
+ mSurface.release();
+ }
+ return;
+ }
if (!mInvalidated) {
return;
}
@@ -1314,6 +1372,7 @@
}
}
+ final boolean showSurface;
// Draw without holding WindowManagerGlobalLock.
if (alpha > 0) {
Canvas canvas = null;
@@ -1329,18 +1388,38 @@
mPaint.setAlpha(alpha);
canvas.drawPath(drawingBounds.getBoundaryPath(), mPaint);
mSurface.unlockCanvasAndPost(canvas);
- t.show(mSurfaceControl);
+ if (DRAW_IN_WM_LOCK) {
+ t.show(mSurfaceControl);
+ return;
+ }
+ showSurface = true;
} else {
- t.hide(mSurfaceControl);
+ if (DRAW_IN_WM_LOCK) {
+ t.hide(mSurfaceControl);
+ return;
+ }
+ showSurface = false;
+ }
+
+ if (showSurface && !mLastSurfaceShown) {
+ mTransaction.show(mSurfaceControl).apply();
+ mLastSurfaceShown = true;
+ } else if (!showSurface && mLastSurfaceShown) {
+ mTransaction.hide(mSurfaceControl).apply();
+ mLastSurfaceShown = false;
}
}
+ @GuardedBy("mService.mGlobalLock")
void releaseSurface() {
- if (mBlastBufferQueue != null) {
- mBlastBufferQueue.destroy();
+ mBlastBufferQueue.destroy();
+ if (DRAW_IN_WM_LOCK) {
+ mService.mTransactionFactory.get().remove(mSurfaceControl).apply();
+ mSurface.release();
+ return;
}
- mService.mTransactionFactory.get().remove(mSurfaceControl).apply();
- mSurface.release();
+ // Post to perform cleanup on the thread which handles mSurface.
+ mService.mAnimationHandler.post(this);
}
void dump(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index febcc05..5fa2610 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2181,7 +2181,8 @@
// The result receiver is the transition receiver, which will handle the shared element
// exit transition.
mHasSceneTransition = options.getAnimationType() == ANIM_SCENE_TRANSITION
- && options.getResultReceiver() != null;
+ && options.getSceneTransitionInfo() != null
+ && options.getSceneTransitionInfo().getResultReceiver() != null;
final PendingIntent usageReport = options.getUsageTimeReport();
if (usageReport != null) {
appTimeTracker = new AppTimeTracker(usageReport);
@@ -5178,16 +5179,15 @@
return mPendingOptions;
}
- ActivityOptions takeOptions() {
- if (DEBUG_TRANSITION) Slog.i(TAG, "Taking options for " + this + " callers="
- + Debug.getCallers(6));
+ ActivityOptions.SceneTransitionInfo takeSceneTransitionInfo() {
+ if (DEBUG_TRANSITION) {
+ Slog.i(TAG, "Taking SceneTransitionInfo for " + this + " callers="
+ + Debug.getCallers(6));
+ }
if (mPendingOptions == null) return null;
final ActivityOptions opts = mPendingOptions;
mPendingOptions = null;
- // Strip sensitive information from options before sending it to app.
- opts.setRemoteTransition(null);
- opts.setRemoteAnimationAdapter(null);
- return opts;
+ return opts.getSceneTransitionInfo();
}
RemoteTransition takeRemoteTransition() {
@@ -6153,7 +6153,7 @@
try {
mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
- StartActivityItem.obtain(token, takeOptions()));
+ StartActivityItem.obtain(token, takeSceneTransitionInfo()));
} catch (Exception e) {
Slog.w(TAG, "Exception thrown sending start: " + intent.getComponent(), e);
}
@@ -6258,8 +6258,11 @@
void handleAlreadyVisible() {
try {
- if (returningOptions != null) {
- app.getThread().scheduleOnNewActivityOptions(token, returningOptions.toBundle());
+ if (returningOptions != null
+ && returningOptions.getAnimationType() == ANIM_SCENE_TRANSITION
+ && returningOptions.getSceneTransitionInfo() != null) {
+ app.getThread().scheduleOnNewSceneTransitionInfo(token,
+ returningOptions.getSceneTransitionInfo());
}
} catch(RemoteException e) {
}
@@ -6608,10 +6611,6 @@
return hasProcess() && !app.isCrashing() && !app.isNotResponding();
}
- void startFreezingScreenLocked(int configChanges) {
- startFreezingScreenLocked(app, configChanges);
- }
-
void startFreezingScreenLocked(WindowProcessController app, int configChanges) {
if (mayFreezeScreenLocked(app)) {
if (getParent() == null) {
@@ -8095,12 +8094,8 @@
* Set the last reported configuration to the client. Should be called whenever
* a new merged configuration is sent to the client for this activity.
*/
- void setLastReportedConfiguration(@NonNull MergedConfiguration config) {
- setLastReportedConfiguration(config.getGlobalConfiguration(),
- config.getOverrideConfiguration());
- }
-
- private void setLastReportedConfiguration(Configuration global, Configuration override) {
+ void setLastReportedConfiguration(@NonNull Configuration global,
+ @NonNull Configuration override) {
mLastReportedConfiguration.setConfiguration(global, override);
}
@@ -9634,7 +9629,7 @@
configChangeFlags |= changes;
if (mVisible && mAtmService.mTmpUpdateConfigurationResult.mIsUpdating
&& !mTransitionController.isShellTransitionsEnabled()) {
- startFreezingScreenLocked(mAtmService.mTmpUpdateConfigurationResult.changes);
+ startFreezingScreenLocked(app, mAtmService.mTmpUpdateConfigurationResult.changes);
}
final boolean displayMayChange = mTmpConfig.windowConfiguration.getDisplayRotation()
!= getWindowConfiguration().getDisplayRotation()
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 1872f6e..ec0e3e7 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -134,7 +134,6 @@
import android.os.WorkSource;
import android.provider.MediaStore;
import android.util.ArrayMap;
-import android.util.MergedConfiguration;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -820,8 +819,6 @@
proc.pauseConfigurationDispatch();
try {
- r.startFreezingScreenLocked(proc, 0);
-
// schedule launch ticks to collect information about slow apps.
r.startLaunchTickingLocked();
r.lastLaunchTime = SystemClock.uptimeMillis();
@@ -908,13 +905,9 @@
r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
mService.getAppWarningsLocked().onStartActivity(r);
- // Because we could be starting an Activity in the system process this may not go
- // across a Binder interface which would create a new Configuration. Consequently
- // we have to always create a new Configuration here.
final Configuration procConfig = proc.prepareConfigurationForLaunchingActivity();
- final MergedConfiguration mergedConfiguration = new MergedConfiguration(
- procConfig, r.getMergedOverrideConfiguration());
- r.setLastReportedConfiguration(mergedConfiguration);
+ final Configuration overrideConfig = r.getMergedOverrideConfiguration();
+ r.setLastReportedConfiguration(procConfig, overrideConfig);
logIfTransactionTooLarge(r.intent, r.getSavedState());
@@ -932,13 +925,10 @@
final int deviceId = getDeviceIdForDisplayId(r.getDisplayId());
final LaunchActivityItem launchActivityItem = LaunchActivityItem.obtain(r.token,
r.intent, System.identityHashCode(r), r.info,
- // TODO: Have this take the merged configuration instead of separate global
- // and override configs.
- mergedConfiguration.getGlobalConfiguration(),
- mergedConfiguration.getOverrideConfiguration(), deviceId,
+ procConfig, overrideConfig, deviceId,
r.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor,
proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(),
- results, newIntents, r.takeOptions(), isTransitionForward,
+ results, newIntents, r.takeSceneTransitionInfo(), isTransitionForward,
proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
r.shareableActivityToken, r.getLaunchedFromBubble(), fragmentToken);
diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
index 975fdc0..0f9e5b0 100644
--- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
+++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
@@ -179,6 +179,12 @@
if (physicalDisplayUpdated) {
onDisplayUpdated(transition, fromRotation, startBounds);
} else {
+ final TransitionRequestInfo.DisplayChange displayChange =
+ getCurrentDisplayChange(fromRotation, startBounds);
+ mDisplayContent.mTransitionController.requestStartTransition(transition,
+ /* startTask= */ null, /* remoteTransition= */ null, displayChange);
+ mDisplayContent.mTransitionController.setDisplaySyncMethod(displayChange,
+ mDisplayContent);
transition.setAllReady();
}
});
@@ -204,15 +210,8 @@
return new DisplayInfo(mNonOverrideDisplayInfo);
}
- /**
- * Called when physical display is updated, this could happen e.g. on foldable
- * devices when the physical underlying display is replaced. This method should be called
- * when the new display info is already applied to the WM hierarchy.
- *
- * @param fromRotation rotation before the display change
- * @param startBounds display bounds before the display change
- */
- private void onDisplayUpdated(@NonNull Transition transition, int fromRotation,
+ @NonNull
+ private TransitionRequestInfo.DisplayChange getCurrentDisplayChange(int fromRotation,
@NonNull Rect startBounds) {
final Rect endBounds = new Rect(0, 0, mDisplayContent.mInitialDisplayWidth,
mDisplayContent.mInitialDisplayHeight);
@@ -224,6 +223,23 @@
displayChange.setEndAbsBounds(endBounds);
displayChange.setStartRotation(fromRotation);
displayChange.setEndRotation(toRotation);
+ return displayChange;
+ }
+
+ /**
+ * Called when physical display is updated, this could happen e.g. on foldable
+ * devices when the physical underlying display is replaced. This method should be called
+ * when the new display info is already applied to the WM hierarchy.
+ *
+ * @param fromRotation rotation before the display change
+ * @param startBounds display bounds before the display change
+ */
+ private void onDisplayUpdated(@NonNull Transition transition, int fromRotation,
+ @NonNull Rect startBounds) {
+ final int toRotation = mDisplayContent.getRotation();
+
+ final TransitionRequestInfo.DisplayChange displayChange =
+ getCurrentDisplayChange(fromRotation, startBounds);
displayChange.setPhysicalDisplayChanged(true);
mDisplayContent.mTransitionController.requestStartTransition(transition,
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 460a68f..63ca592 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -40,6 +40,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_CONSUME_IME_INSETS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IMMERSIVE_CONFIRMATION_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
@@ -959,15 +960,17 @@
case TYPE_BASE_APPLICATION:
- // A non-translucent main app window isn't allowed to fit insets, as it would create
- // a hole on the display!
+ // A non-translucent main app window isn't allowed to fit insets or display cutouts,
+ // as it would create a hole on the display!
if (attrs.isFullscreen() && win.mActivityRecord != null
&& win.mActivityRecord.fillsParent()
- && (win.mAttrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0
- && attrs.getFitInsetsTypes() != 0) {
+ && (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0
+ && (attrs.getFitInsetsTypes() != 0
+ || (attrs.privateFlags & PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED) != 0
+ && attrs.layoutInDisplayCutoutMode
+ != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS)) {
throw new IllegalArgumentException("Illegal attributes: Main activity window"
- + " that isn't translucent trying to fit insets: "
- + attrs.getFitInsetsTypes()
+ + " that isn't translucent trying to fit insets or display cutouts."
+ " attrs=" + attrs);
}
break;
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index c3aca6f..708d63e 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -634,8 +634,8 @@
}
/** Sets the sync method for the display change. */
- private void setDisplaySyncMethod(@NonNull TransitionRequestInfo.DisplayChange displayChange,
- @NonNull Transition displayTransition, @NonNull DisplayContent displayContent) {
+ void setDisplaySyncMethod(@NonNull TransitionRequestInfo.DisplayChange displayChange,
+ @NonNull DisplayContent displayContent) {
final Rect startBounds = displayChange.getStartAbsBounds();
final Rect endBounds = displayChange.getEndAbsBounds();
if (startBounds == null || endBounds == null) return;
@@ -686,7 +686,7 @@
trigger != null ? trigger.asTask() : null, remoteTransition, displayChange);
if (newTransition != null && displayChange != null && trigger != null
&& trigger.asDisplayContent() != null) {
- setDisplaySyncMethod(displayChange, newTransition, trigger.asDisplayContent());
+ setDisplaySyncMethod(displayChange, trigger.asDisplayContent());
}
}
if (trigger != null) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e28262d..bdea1bc 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3938,7 +3938,13 @@
@Nullable
BLASTSyncEngine.SyncGroup getSyncGroup() {
if (mSyncGroup != null) return mSyncGroup;
- if (mParent != null) return mParent.getSyncGroup();
+ WindowContainer<?> parent = mParent;
+ while (parent != null) {
+ if (parent.mSyncGroup != null) {
+ return parent.mSyncGroup;
+ }
+ parent = parent.mParent;
+ }
return null;
}
@@ -3972,7 +3978,7 @@
* @param cancel If true, this is being finished because it is leaving the sync group rather
* than due to the sync group completing.
*/
- void finishSync(Transaction outMergedTransaction, BLASTSyncEngine.SyncGroup group,
+ void finishSync(Transaction outMergedTransaction, @Nullable BLASTSyncEngine.SyncGroup group,
boolean cancel) {
if (mSyncState == SYNC_STATE_NONE) return;
final BLASTSyncEngine.SyncGroup syncGroup = getSyncGroup();
@@ -4000,7 +4006,8 @@
if (!isVisibleRequested()) {
return true;
}
- if (mSyncState == SYNC_STATE_NONE) {
+ if (mSyncState == SYNC_STATE_NONE && getSyncGroup() != null) {
+ Slog.i(TAG, "prepareSync in isSyncFinished: " + this);
prepareSync();
}
if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW) {
@@ -4059,16 +4066,18 @@
}
if (newParent == null) {
// This is getting removed.
+ final BLASTSyncEngine.SyncGroup syncGroup = getSyncGroup();
if (oldParent.mSyncState != SYNC_STATE_NONE) {
// In order to keep the transaction in sync, merge it into the parent.
- finishSync(oldParent.mSyncTransaction, getSyncGroup(), true /* cancel */);
- } else if (mSyncGroup != null) {
- // This is watched directly by the sync-group, so merge this transaction into
- // into the sync-group so it isn't lost
- finishSync(mSyncGroup.getOrphanTransaction(), mSyncGroup, true /* cancel */);
+ finishSync(oldParent.mSyncTransaction, syncGroup, true /* cancel */);
+ } else if (syncGroup != null) {
+ // This is watched by the sync-group, so merge this transaction into the
+ // sync-group for not losing the operations in the transaction.
+ finishSync(syncGroup.getOrphanTransaction(), syncGroup, true /* cancel */);
} else {
- throw new IllegalStateException("This container is in sync mode without a sync"
- + " group: " + this);
+ Slog.wtf(TAG, this + " is in sync mode without a sync group");
+ // Make sure the removal transaction take effect.
+ finishSync(getPendingTransaction(), null /* group */, true /* cancel */);
}
return;
} else if (mSyncGroup == null) {
diff --git a/services/core/xsd/display-layout-config/display-layout-config.xsd b/services/core/xsd/display-layout-config/display-layout-config.xsd
index 4e95465..73c13ce 100644
--- a/services/core/xsd/display-layout-config/display-layout-config.xsd
+++ b/services/core/xsd/display-layout-config/display-layout-config.xsd
@@ -49,7 +49,7 @@
<xs:complexType name="display">
<xs:sequence>
- <xs:element name="address" type="xs:nonNegativeInteger"/>
+ <xs:group ref="displayReference" minOccurs="1" maxOccurs="1"/>
<xs:element name="position" type="xs:string" minOccurs="0" maxOccurs="1" />
<xs:element name="brightnessThrottlingMapId" type="xs:string" minOccurs="0" maxOccurs="1" />
<xs:element name="powerThrottlingMapId" type="xs:string" minOccurs="0" maxOccurs="1" />
@@ -67,4 +67,11 @@
</xs:simpleType>
</xs:attribute>
</xs:complexType>
+
+ <xs:group name="displayReference">
+ <xs:choice>
+ <xs:element name="address" type="xs:nonNegativeInteger" minOccurs="0"/>
+ <xs:element name="port" type="xs:nonNegativeInteger" minOccurs="0"/>
+ </xs:choice>
+ </xs:group>
</xs:schema>
diff --git a/services/core/xsd/display-layout-config/schema/current.txt b/services/core/xsd/display-layout-config/schema/current.txt
index 195cae5..0d2f6a8 100644
--- a/services/core/xsd/display-layout-config/schema/current.txt
+++ b/services/core/xsd/display-layout-config/schema/current.txt
@@ -3,22 +3,24 @@
public class Display {
ctor public Display();
- method public java.math.BigInteger getAddress();
+ method public java.math.BigInteger getAddress_optional();
method public String getBrightnessThrottlingMapId();
method public String getDisplayGroup();
method public java.math.BigInteger getLeadDisplayAddress();
+ method public java.math.BigInteger getPort_optional();
method public String getPosition();
method public String getPowerThrottlingMapId();
method public String getRefreshRateThermalThrottlingMapId();
method public String getRefreshRateZoneId();
method public boolean isDefaultDisplay();
method public boolean isEnabled();
- method public void setAddress(java.math.BigInteger);
+ method public void setAddress_optional(java.math.BigInteger);
method public void setBrightnessThrottlingMapId(String);
method public void setDefaultDisplay(boolean);
method public void setDisplayGroup(String);
method public void setEnabled(boolean);
method public void setLeadDisplayAddress(java.math.BigInteger);
+ method public void setPort_optional(java.math.BigInteger);
method public void setPosition(String);
method public void setPowerThrottlingMapId(String);
method public void setRefreshRateThermalThrottlingMapId(String);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index e0232b1..14dc0eb 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -32,6 +32,7 @@
import android.annotation.Nullable;
import android.app.AppGlobals;
import android.app.BroadcastOptions;
+import android.app.admin.BooleanPolicyValue;
import android.app.admin.DevicePolicyIdentifiers;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyState;
@@ -142,6 +143,67 @@
mAdminPolicySize = new SparseArray<>();
}
+ private void maybeForceEnforcementRefreshLocked(@NonNull PolicyDefinition<?> policyDefinition) {
+ try {
+ if (shouldForceEnforcementRefresh(policyDefinition)) {
+ // This is okay because it's only true for user restrictions which are all <Boolean>
+ forceEnforcementRefreshLocked((PolicyDefinition<Boolean>) policyDefinition);
+ }
+ } catch (Throwable e) {
+ // Catch any possible exceptions just to be on the safe side
+ Log.e(TAG, "Exception throw during maybeForceEnforcementRefreshLocked", e);
+ }
+ }
+
+ private boolean shouldForceEnforcementRefresh(@NonNull PolicyDefinition<?> policyDefinition) {
+ // These are all "not nullable" but for the purposes of maximum safety for a lightly tested
+ // change we check here
+ if (policyDefinition == null) {
+ return false;
+ }
+ PolicyKey policyKey = policyDefinition.getPolicyKey();
+ if (policyKey == null) {
+ return false;
+ }
+
+ if (policyKey instanceof UserRestrictionPolicyKey) {
+ // b/307481299 We must force all user restrictions to re-sync local
+ // + global on each set/clear
+ return true;
+ }
+
+ return false;
+ }
+
+ private void forceEnforcementRefreshLocked(PolicyDefinition<Boolean> policyDefinition) {
+ Binder.withCleanCallingIdentity(() -> {
+ // Sync global state
+ PolicyValue<Boolean> globalValue = new BooleanPolicyValue(false);
+ try {
+ PolicyState<Boolean> policyState = getGlobalPolicyStateLocked(policyDefinition);
+ globalValue = policyState.getCurrentResolvedPolicy();
+ } catch (IllegalArgumentException e) {
+ // Expected for local-only policies
+ }
+
+ enforcePolicy(policyDefinition, globalValue, UserHandle.USER_ALL);
+
+ // Loop through each user and sync that user's state
+ for (UserInfo user : mUserManager.getUsers()) {
+ PolicyValue<Boolean> localValue = new BooleanPolicyValue(false);
+ try {
+ PolicyState<Boolean> localPolicyState = getLocalPolicyStateLocked(
+ policyDefinition, user.id);
+ localValue = localPolicyState.getCurrentResolvedPolicy();
+ } catch (IllegalArgumentException e) {
+ // Expected for global-only policies
+ }
+
+ enforcePolicy(policyDefinition, localValue, user.id);
+ }
+ });
+ }
+
/**
* Set the policy for the provided {@code policyDefinition} (see {@link PolicyDefinition}) and
* {@code enforcingAdmin} to the provided {@code value}.
@@ -188,6 +250,7 @@
// No need to notify admins as no new policy is actually enforced, we're just filling in
// the data structures.
if (!skipEnforcePolicy) {
+ maybeForceEnforcementRefreshLocked(policyDefinition);
if (policyChanged) {
onLocalPolicyChangedLocked(policyDefinition, enforcingAdmin, userId);
}
@@ -278,6 +341,7 @@
Objects.requireNonNull(enforcingAdmin);
synchronized (mLock) {
+ maybeForceEnforcementRefreshLocked(policyDefinition);
if (!hasLocalPolicyLocked(policyDefinition, userId)) {
return;
}
@@ -451,6 +515,7 @@
// No need to notify admins as no new policy is actually enforced, we're just filling in
// the data structures.
if (!skipEnforcePolicy) {
+ maybeForceEnforcementRefreshLocked(policyDefinition);
if (policyChanged) {
onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin);
}
@@ -506,6 +571,7 @@
boolean policyChanged = policyState.removePolicy(enforcingAdmin);
+ maybeForceEnforcementRefreshLocked(policyDefinition);
if (policyChanged) {
onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin);
}
diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp
index 52eae21..a70802a 100644
--- a/services/robotests/Android.bp
+++ b/services/robotests/Android.bp
@@ -57,9 +57,13 @@
],
static_libs: [
"androidx.test.ext.truth",
+ "Settings-robo-testutils",
+ "SettingsLib-robo-testutils",
],
instrumentation_for: "FrameworksServicesLib",
+
+ upstream: true,
}
filegroup {
diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
index 8b9efb3..569786b 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -57,6 +57,8 @@
// Include the testing libraries
libs: [
"mockito-robolectric-prebuilt",
+ "Settings-robo-testutils",
+ "SettingsLib-robo-testutils",
"platform-test-annotations",
"testng",
"truth",
@@ -64,4 +66,6 @@
instrumentation_for: "BackupFrameworksServicesLib",
+ upstream: true,
+
}
diff --git a/services/robotests/backup/config/robolectric.properties b/services/robotests/backup/config/robolectric.properties
index 850557a..1ebf6d4 100644
--- a/services/robotests/backup/config/robolectric.properties
+++ b/services/robotests/backup/config/robolectric.properties
@@ -1 +1,3 @@
-sdk=NEWEST_SDK
\ No newline at end of file
+sdk=NEWEST_SDK
+looperMode=LEGACY
+shadows=com.android.server.testing.shadows.FrameworkShadowLooper
diff --git a/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
index ee5a534..6839a06 100644
--- a/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
@@ -57,6 +57,7 @@
ShadowBackupDataOutput.class,
ShadowEnvironment.class,
ShadowFullBackup.class,
+ ShadowSigningInfo.class,
})
public class AppMetadataBackupWriterTest {
private static final String TEST_PACKAGE = "com.test.package";
diff --git a/services/robotests/backup/src/com/android/server/backup/fullbackup/ShadowSigningInfo.java b/services/robotests/backup/src/com/android/server/backup/fullbackup/ShadowSigningInfo.java
new file mode 100644
index 0000000..53d807c
--- /dev/null
+++ b/services/robotests/backup/src/com/android/server/backup/fullbackup/ShadowSigningInfo.java
@@ -0,0 +1,27 @@
+/*
+ * 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.backup.fullbackup;
+
+import static android.os.Build.VERSION_CODES.P;
+
+import android.content.pm.SigningInfo;
+
+import org.robolectric.annotation.Implements;
+
+@Implements(value = SigningInfo.class, minSdk = P)
+public class ShadowSigningInfo {
+}
diff --git a/services/robotests/src/com/android/server/location/gnss/NtpNetworkTimeHelperTest.java b/services/robotests/src/com/android/server/location/gnss/NtpNetworkTimeHelperTest.java
index 4949091..0092763 100644
--- a/services/robotests/src/com/android/server/location/gnss/NtpNetworkTimeHelperTest.java
+++ b/services/robotests/src/com/android/server/location/gnss/NtpNetworkTimeHelperTest.java
@@ -35,6 +35,7 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.LooperMode;
import org.robolectric.shadows.ShadowLooper;
import java.util.concurrent.CountDownLatch;
@@ -45,6 +46,7 @@
*/
@RunWith(RobolectricTestRunner.class)
@Presubmit
+@LooperMode(LooperMode.Mode.LEGACY)
public class NtpNetworkTimeHelperTest {
private static final long MOCK_NTP_TIME = 1519930775453L;
diff --git a/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java
index 16d16cd..3681bd4 100644
--- a/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java
+++ b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java
@@ -21,12 +21,15 @@
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
+import org.robolectric.shadows.LooperShadowPicker;
+import org.robolectric.shadows.ShadowLegacyLooper;
import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.shadows.ShadowPausedLooper;
import java.util.Optional;
-@Implements(value = Looper.class)
-public class FrameworkShadowLooper extends ShadowLooper {
+@Implements(value = Looper.class, shadowPicker = FrameworkShadowLooper.Picker.class)
+public class FrameworkShadowLooper extends ShadowLegacyLooper {
@RealObject private Looper mLooper;
private Optional<Boolean> mIsCurrentThread = Optional.empty();
@@ -45,4 +48,10 @@
}
return Thread.currentThread() == mLooper.getThread();
}
+
+ public static class Picker extends LooperShadowPicker<ShadowLooper> {
+ public Picker() {
+ super(FrameworkShadowLooper.class, ShadowPausedLooper.class);
+ }
+ }
}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
index 4a99486..1da6759 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java
@@ -95,7 +95,6 @@
sPackageAppEnabledStates.put(packageName, Integer.valueOf(newState)); // flags unused here.
}
- @Override
protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
throws NameNotFoundException {
if (!sPackageInfos.containsKey(packageName)) {
diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp
index ffe6dc5..56423b9 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -40,6 +40,7 @@
"frameworks-base-testutils",
"mockito-target-extended-minus-junit4",
"platform-test-annotations",
+ "ravenwood-junit",
"services.core",
"service-permission.stubs.system_server",
"servicestests-core-utils",
@@ -66,6 +67,28 @@
},
}
+android_ravenwood_test {
+ name: "FrameworksInputMethodSystemServerTests_host",
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.test.rules",
+ "framework",
+ "mockito_ravenwood",
+ "ravenwood-runtime",
+ "ravenwood-utils",
+ "services",
+ ],
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
+ srcs: [
+ "src/com/android/server/inputmethod/**/ClientControllerTest.java",
+ ],
+ sdk_version: "test_current",
+ auto_gen_config: true,
+}
+
android_test {
name: "FrameworksImeTests",
defaults: [
@@ -88,6 +111,7 @@
"frameworks-base-testutils",
"mockito-target-extended-minus-junit4",
"platform-test-annotations",
+ "ravenwood-junit",
"services.core",
"service-permission.stubs.system_server",
"servicestests-core-utils",
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java
new file mode 100644
index 0000000..3c8f5c9
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2023 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.inputmethod;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.PackageManagerInternal;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.view.Display;
+import android.view.inputmethod.InputBinding;
+
+import com.android.internal.inputmethod.IInputMethodClient;
+import com.android.internal.inputmethod.IRemoteInputConnection;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+
+// This test is designed to run on both device and host (Ravenwood) side.
+public final class ClientControllerTest {
+ private static final int ANY_DISPLAY_ID = Display.DEFAULT_DISPLAY;
+ private static final int ANY_CALLER_UID = 1;
+ private static final int ANY_CALLER_PID = 1;
+
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true).build();
+
+ @Mock
+ private PackageManagerInternal mMockPackageManagerInternal;
+
+ @Mock(extraInterfaces = IBinder.class)
+ private IInputMethodClient mClient;
+
+ @Mock
+ private IRemoteInputConnection mConnection;
+
+ @Mock
+ private IBinder.DeathRecipient mDeathRecipient;
+
+ private Handler mHandler;
+
+ private ClientController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mHandler = new Handler(Looper.getMainLooper());
+ mController = new ClientController(mMockPackageManagerInternal);
+ when(mClient.asBinder()).thenReturn((IBinder) mClient);
+ }
+
+ @Test
+ // TODO(b/314150112): Enable host side mode for this test once b/315544364 is fixed.
+ @IgnoreUnderRavenwood(blockedBy = {InputBinding.class, IInputMethodClientInvoker.class})
+ public void testAddClient_cannotAddTheSameClientTwice() {
+ var invoker = IInputMethodClientInvoker.create(mClient, mHandler);
+
+ synchronized (ImfLock.class) {
+ mController.addClient(invoker, mConnection, ANY_DISPLAY_ID, mDeathRecipient,
+ ANY_CALLER_UID, ANY_CALLER_PID);
+
+ SecurityException thrown = assertThrows(SecurityException.class,
+ () -> {
+ synchronized (ImfLock.class) {
+ mController.addClient(invoker, mConnection, ANY_DISPLAY_ID,
+ mDeathRecipient, ANY_CALLER_UID, ANY_CALLER_PID);
+ }
+ });
+ assertThat(thrown.getMessage()).isEqualTo(
+ "uid=1/pid=1/displayId=0 is already registered");
+ }
+ }
+}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 3199e06..438bea4 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -22,6 +22,7 @@
import static com.android.internal.inputmethod.SoftInputShowHideReason.HIDE_SOFT_INPUT;
import static com.android.internal.inputmethod.SoftInputShowHideReason.HIDE_SWITCH_USER;
import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_SOFT_INPUT;
+import static com.android.server.inputmethod.ClientController.ClientState;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_EXPLICIT;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_NOT_ALWAYS;
@@ -68,8 +69,7 @@
super.setUp();
mVisibilityApplier =
(DefaultImeVisibilityApplier) mInputMethodManagerService.getVisibilityApplier();
- mInputMethodManagerService.setAttachedClientForTesting(
- mock(InputMethodManagerService.ClientState.class));
+ mInputMethodManagerService.setAttachedClientForTesting(mock(ClientState.class));
}
@Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DeviceStateToLayoutMapTest.java b/services/tests/displayservicetests/src/com/android/server/display/DeviceStateToLayoutMapTest.java
index 8cc3408..567792e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DeviceStateToLayoutMapTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DeviceStateToLayoutMapTest.java
@@ -25,6 +25,7 @@
import androidx.test.filters.SmallTest;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.layout.DisplayIdProducer;
import com.android.server.display.layout.Layout;
@@ -45,6 +46,7 @@
private DeviceStateToLayoutMap mDeviceStateToLayoutMap;
@Mock DisplayIdProducer mDisplayIdProducerMock;
+ @Mock DisplayManagerFlags mMockFlags;
@Before
public void setUp() throws IOException {
@@ -52,7 +54,7 @@
Mockito.when(mDisplayIdProducerMock.getId(false)).thenReturn(1);
- setupDeviceStateToLayoutMap();
+ setupDeviceStateToLayoutMap(getContent());
}
//////////////////
@@ -268,6 +270,41 @@
IllegalArgumentException.class, () -> layout.postProcessLocked());
}
+ @Test
+ public void testPortInLayout_disabledFlag() {
+ Mockito.when(mMockFlags.isPortInDisplayLayoutEnabled()).thenReturn(false);
+ assertThrows("Expected IllegalArgumentException when using <port>",
+ IllegalArgumentException.class,
+ () -> setupDeviceStateToLayoutMap(getPortContent()));
+ }
+
+ @Test
+ public void testPortInLayout_readLayout() throws Exception {
+ Mockito.when(mMockFlags.isPortInDisplayLayoutEnabled()).thenReturn(true);
+ setupDeviceStateToLayoutMap(getPortContent());
+
+ Layout configLayout = mDeviceStateToLayoutMap.get(0);
+
+ Layout testLayout = new Layout();
+ testLayout.createDisplayLocked(DisplayAddress.fromPortAndModel(123, null),
+ /* isDefault= */ true, /* isEnabled= */ true, /* displayGroupName= */ null,
+ mDisplayIdProducerMock, Layout.Display.POSITION_UNKNOWN,
+ /* leadDisplayAddress= */ null, /* brightnessThrottlingMapId= */ null,
+ /* refreshRateZoneId= */ null,
+ /* refreshRateThermalThrottlingMapId= */ null,
+ /* powerThrottlingMapId= */ null);
+ testLayout.createDisplayLocked(DisplayAddress.fromPhysicalDisplayId(78910L),
+ /* isDefault= */ false, /* isEnabled= */ false, /* displayGroupName= */ null,
+ mDisplayIdProducerMock, Layout.Display.POSITION_UNKNOWN,
+ /* leadDisplayAddress= */ null, /* brightnessThrottlingMapId= */ null,
+ /* refreshRateZoneId= */ null,
+ /* refreshRateThermalThrottlingMapId= */ null,
+ /* powerThrottlingMapId= */ null);
+ testLayout.postProcessLocked();
+
+ assertEquals(testLayout, configLayout);
+ }
+
////////////////////
// Helper Methods //
////////////////////
@@ -287,13 +324,28 @@
/* powerThrottlingMapId= */ null);
}
- private void setupDeviceStateToLayoutMap() throws IOException {
+ private void setupDeviceStateToLayoutMap(String content) throws IOException {
Path tempFile = Files.createTempFile("device_state_layout_map", ".tmp");
- Files.write(tempFile, getContent().getBytes(StandardCharsets.UTF_8));
- mDeviceStateToLayoutMap = new DeviceStateToLayoutMap(mDisplayIdProducerMock,
+ Files.write(tempFile, content.getBytes(StandardCharsets.UTF_8));
+ mDeviceStateToLayoutMap = new DeviceStateToLayoutMap(mDisplayIdProducerMock, mMockFlags,
tempFile.toFile());
}
+ private String getPortContent() {
+ return "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<layouts>\n"
+ + "<layout>\n"
+ + "<state>0</state> \n"
+ + "<display enabled=\"true\" defaultDisplay=\"true\">\n"
+ + "<port>123</port>\n"
+ + "</display>\n"
+ + "<display enabled=\"false\">\n"
+ + "<address>78910</address>\n"
+ + "</display>\n"
+ + "</layout>\n"
+ + "</layouts>\n";
+ }
+
private String getContent() {
return "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<layouts>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 28ec896..bed6f92 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -49,8 +49,9 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -83,7 +84,6 @@
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
import java.io.File;
import java.io.InputStream;
@@ -111,14 +111,14 @@
private final DisplayIdProducer mIdProducer = (isDefault) ->
isDefault ? DEFAULT_DISPLAY : sNextNonDefaultDisplayId++;
+ private DeviceStateToLayoutMap mDeviceStateToLayoutMapSpy;
+
@Mock LogicalDisplayMapper.Listener mListenerMock;
@Mock Context mContextMock;
@Mock FoldSettingProvider mFoldSettingProviderMock;
@Mock Resources mResourcesMock;
@Mock IPowerManager mIPowerManagerMock;
@Mock IThermalService mIThermalServiceMock;
- @Spy DeviceStateToLayoutMap mDeviceStateToLayoutMapSpy =
- new DeviceStateToLayoutMap(mIdProducer, NON_EXISTING_FILE);
@Mock DisplayManagerFlags mFlagsMock;
@Mock DisplayAdapter mDisplayAdapterMock;
@@ -131,6 +131,8 @@
System.setProperty("dexmaker.share_classloader", "true");
MockitoAnnotations.initMocks(this);
+ mDeviceStateToLayoutMapSpy =
+ spy(new DeviceStateToLayoutMap(mIdProducer, mFlagsMock, NON_EXISTING_FILE));
mDisplayDeviceRepo = new DisplayDeviceRepository(
new DisplayManagerService.SyncRoot(),
new PersistentDataStore(new PersistentDataStore.Injector() {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index 60a0c03..a0e5fd8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -3434,6 +3434,11 @@
return mSensorManagerInternal;
}
+ @Override
+ public VotesStatsReporter getVotesStatsReporter(boolean refreshRateVotingTelemetryEnabled) {
+ return null;
+ }
+
protected Display createDisplay(int id) {
return new Display(DisplayManagerGlobal.getInstance(), id, mDisplayInfo,
ApplicationProvider.getApplicationContext().getResources());
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
index f677401..2815fcb 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
@@ -58,7 +58,7 @@
private RegisteringFakesInjector mInjector = new RegisteringFakesInjector();
private final TestHandler mHandler = new TestHandler(null);
- private final VotesStorage mStorage = new VotesStorage(() -> {});
+ private final VotesStorage mStorage = new VotesStorage(() -> {}, null);
@Before
public void setUp() {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
index 50e2392..1f6f1a4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
@@ -51,7 +51,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mVotesStorage = new VotesStorage(mVotesListener);
+ mVotesStorage = new VotesStorage(mVotesListener, null);
}
@Test
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 113511e..321d945 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -73,6 +73,7 @@
// TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows
"testng",
"compatibility-device-util-axt",
+ "flag-junit",
],
libs: [
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
index 57326b2..d08cdc7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -343,10 +343,12 @@
List<ApexManager.ScanResult> scanResults = scanApexInfos(apexInfo);
mApexManager.notifyScanResult(scanResults);
- assertThat(mApexManager.getApkInApexInstallError(activeApex.apexModuleName)).isNull();
+ final String apexPackageName = mApexManager.getActivePackageNameForApexModuleName(
+ activeApex.apexModuleName);
+ assertThat(mApexManager.getApkInApexInstallError(apexPackageName)).isNull();
mApexManager.reportErrorWithApkInApex(activeApex.apexDirectory.getAbsolutePath(),
"Some random error");
- assertThat(mApexManager.getApkInApexInstallError(activeApex.apexModuleName))
+ assertThat(mApexManager.getApkInApexInstallError(apexPackageName))
.isEqualTo("Some random error");
}
@@ -370,9 +372,11 @@
List<ApexManager.ScanResult> scanResults = scanApexInfos(apexInfo);
mApexManager.notifyScanResult(scanResults);
- assertThat(mApexManager.getApksInApex(activeApex.apexModuleName)).isEmpty();
+ final String apexPackageName = mApexManager.getActivePackageNameForApexModuleName(
+ activeApex.apexModuleName);
+ assertThat(mApexManager.getApksInApex(apexPackageName)).isEmpty();
mApexManager.registerApkInApex(fakeApkInApex);
- assertThat(mApexManager.getApksInApex(activeApex.apexModuleName)).isEmpty();
+ assertThat(mApexManager.getApksInApex(apexPackageName)).isEmpty();
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
index 9851bc1..c42c735 100644
--- a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
@@ -16,24 +16,23 @@
package com.android.server.trust;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.argThat;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-
import android.Manifest;
import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.admin.DevicePolicyManager;
import android.app.trust.ITrustListener;
import android.app.trust.ITrustManager;
import android.content.BroadcastReceiver;
@@ -45,13 +44,15 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
-import android.os.test.TestLooper;
+import android.os.UserManager;
import android.provider.Settings;
import android.service.trust.TrustAgentService;
import android.testing.TestableContext;
@@ -61,12 +62,11 @@
import androidx.test.core.app.ApplicationProvider;
import com.android.internal.widget.LockPatternUtils;
+import com.android.modules.utils.testing.ExtendedMockitoRule;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
-import com.google.android.collect.Lists;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -74,37 +74,60 @@
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
-import org.mockito.MockitoSession;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Random;
+import java.util.Collection;
+import java.util.List;
public class TrustManagerServiceTest {
@Rule
- public MockitoRule mMockitoRule = MockitoJUnit.rule();
+ public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
+ .mockStatic(ServiceManager.class)
+ .mockStatic(WindowManagerGlobal.class)
+ .build();
+
@Rule
public final MockContext mMockContext = new MockContext(
ApplicationProvider.getApplicationContext());
private static final String URI_SCHEME_PACKAGE = "package";
- private static final int TEST_USER_ID = UserHandle.USER_SYSTEM;
+ private static final int TEST_USER_ID = 50;
- private final TestLooper mLooper = new TestLooper();
private final ArrayList<ResolveInfo> mTrustAgentResolveInfoList = new ArrayList<>();
- private final LockPatternUtils mLockPatternUtils = new LockPatternUtils(mMockContext);
- private final TrustManagerService mService = new TrustManagerService(mMockContext);
+ private final ArrayList<ComponentName> mKnownTrustAgents = new ArrayList<>();
+ private final ArrayList<ComponentName> mEnabledTrustAgents = new ArrayList<>();
- @Mock
- private PackageManager mPackageManagerMock;
+ private @Mock ActivityManager mActivityManager;
+ private @Mock DevicePolicyManager mDevicePolicyManager;
+ private @Mock LockPatternUtils mLockPatternUtils;
+ private @Mock PackageManager mPackageManager;
+ private @Mock UserManager mUserManager;
+ private @Mock IWindowManager mWindowManager;
+
+ private HandlerThread mHandlerThread;
+ private TrustManagerService.Injector mInjector;
+ private TrustManagerService mService;
+ private ITrustManager mTrustManager;
@Before
- public void setUp() {
- resetTrustAgentLockSettings();
- LocalServices.addService(SystemServiceManager.class, mock(SystemServiceManager.class));
+ public void setUp() throws Exception {
+ when(mActivityManager.isUserRunning(TEST_USER_ID)).thenReturn(true);
+
+ when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager);
+ when(mLockPatternUtils.isSecure(TEST_USER_ID)).thenReturn(true);
+ when(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).thenReturn(mKnownTrustAgents);
+ when(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).thenReturn(mEnabledTrustAgents);
+ doAnswer(invocation -> {
+ mKnownTrustAgents.clear();
+ mKnownTrustAgents.addAll((Collection<ComponentName>) invocation.getArgument(0));
+ return null;
+ }).when(mLockPatternUtils).setKnownTrustAgents(any(), eq(TEST_USER_ID));
+ doAnswer(invocation -> {
+ mEnabledTrustAgents.clear();
+ mEnabledTrustAgents.addAll((Collection<ComponentName>) invocation.getArgument(0));
+ return null;
+ }).when(mLockPatternUtils).setEnabledTrustAgents(any(), eq(TEST_USER_ID));
ArgumentMatcher<Intent> trustAgentIntentMatcher = new ArgumentMatcher<Intent>() {
@Override
@@ -112,17 +135,42 @@
return TrustAgentService.SERVICE_INTERFACE.equals(argument.getAction());
}
};
- when(mPackageManagerMock.queryIntentServicesAsUser(argThat(trustAgentIntentMatcher),
+ when(mPackageManager.queryIntentServicesAsUser(argThat(trustAgentIntentMatcher),
anyInt(), anyInt())).thenReturn(mTrustAgentResolveInfoList);
- when(mPackageManagerMock.checkPermission(any(), any())).thenReturn(
+ when(mPackageManager.checkPermission(any(), any())).thenReturn(
PackageManager.PERMISSION_GRANTED);
- mMockContext.setMockPackageManager(mPackageManagerMock);
+
+ when(mUserManager.getAliveUsers()).thenReturn(
+ List.of(new UserInfo(TEST_USER_ID, "user", UserInfo.FLAG_FULL)));
+
+ when(mWindowManager.isKeyguardLocked()).thenReturn(true);
+
+ mMockContext.addMockSystemService(ActivityManager.class, mActivityManager);
+ mMockContext.setMockPackageManager(mPackageManager);
+ mMockContext.addMockSystemService(UserManager.class, mUserManager);
+ doReturn(mWindowManager).when(() -> WindowManagerGlobal.getWindowManagerService());
+ LocalServices.addService(SystemServiceManager.class, mock(SystemServiceManager.class));
+
+ grantPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE);
+ grantPermission(Manifest.permission.TRUST_LISTENER);
+
+ mHandlerThread = new HandlerThread("handler");
+ mHandlerThread.start();
+ mInjector = new TrustManagerService.Injector(mLockPatternUtils, mHandlerThread.getLooper());
+ mService = new TrustManagerService(mMockContext, mInjector);
+
+ // Get the ITrustManager from the new TrustManagerService.
+ mService.onStart();
+ ArgumentCaptor<IBinder> binderArgumentCaptor = ArgumentCaptor.forClass(IBinder.class);
+ verify(() -> ServiceManager.addService(eq(Context.TRUST_SERVICE),
+ binderArgumentCaptor.capture(), anyBoolean(), anyInt()));
+ mTrustManager = ITrustManager.Stub.asInterface(binderArgumentCaptor.getValue());
}
@After
public void tearDown() {
- resetTrustAgentLockSettings();
LocalServices.removeServiceForTest(SystemServiceManager.class);
+ mHandlerThread.quit();
}
@Test
@@ -142,10 +190,9 @@
bootService();
- assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
- systemTrustAgent1, systemTrustAgent2);
- assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
- systemTrustAgent1, systemTrustAgent2, userTrustAgent1, userTrustAgent2);
+ assertThat(mEnabledTrustAgents).containsExactly(systemTrustAgent1, systemTrustAgent2);
+ assertThat(mKnownTrustAgents).containsExactly(systemTrustAgent1, systemTrustAgent2,
+ userTrustAgent1, userTrustAgent2);
}
@Test
@@ -162,10 +209,8 @@
bootService();
- assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
- defaultTrustAgent);
- assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
- systemTrustAgent, defaultTrustAgent);
+ assertThat(mEnabledTrustAgents).containsExactly(defaultTrustAgent);
+ assertThat(mKnownTrustAgents).containsExactly(systemTrustAgent, defaultTrustAgent);
}
@Test
@@ -174,16 +219,16 @@
"com.android/.SystemTrustAgent");
ComponentName trustAgent2 = ComponentName.unflattenFromString(
"com.android/.AnotherSystemTrustAgent");
- initializeEnabledAgents(trustAgent1);
+ mEnabledTrustAgents.add(trustAgent1);
+ Settings.Secure.putIntForUser(mMockContext.getContentResolver(),
+ Settings.Secure.TRUST_AGENTS_INITIALIZED, 1, TEST_USER_ID);
addTrustAgent(trustAgent1, /* isSystemApp= */ true);
addTrustAgent(trustAgent2, /* isSystemApp= */ true);
bootService();
- assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
- trustAgent1);
- assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
- trustAgent1, trustAgent2);
+ assertThat(mEnabledTrustAgents).containsExactly(trustAgent1);
+ assertThat(mKnownTrustAgents).containsExactly(trustAgent1, trustAgent2);
}
@Test
@@ -192,17 +237,17 @@
"com.android/.SystemTrustAgent");
ComponentName trustAgent2 = ComponentName.unflattenFromString(
"com.android/.AnotherSystemTrustAgent");
- initializeEnabledAgents(trustAgent1);
- initializeKnownAgents(trustAgent1);
+ Settings.Secure.putIntForUser(mMockContext.getContentResolver(),
+ Settings.Secure.TRUST_AGENTS_INITIALIZED, 1, TEST_USER_ID);
+ Settings.Secure.putIntForUser(mMockContext.getContentResolver(),
+ Settings.Secure.KNOWN_TRUST_AGENTS_INITIALIZED, 1, TEST_USER_ID);
addTrustAgent(trustAgent1, /* isSystemApp= */ true);
addTrustAgent(trustAgent2, /* isSystemApp= */ true);
bootService();
- assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
- trustAgent1, trustAgent2);
- assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
- trustAgent1, trustAgent2);
+ assertThat(mEnabledTrustAgents).containsExactly(trustAgent1, trustAgent2);
+ assertThat(mKnownTrustAgents).containsExactly(trustAgent1, trustAgent2);
}
@Test
@@ -214,10 +259,8 @@
mMockContext.sendPackageChangedBroadcast(newAgentComponentName);
- assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
- newAgentComponentName);
- assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
- newAgentComponentName);
+ assertThat(mEnabledTrustAgents).containsExactly(newAgentComponentName);
+ assertThat(mKnownTrustAgents).containsExactly(newAgentComponentName);
}
@Test
@@ -235,10 +278,8 @@
mMockContext.sendPackageChangedBroadcast(newAgentComponentName);
- assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
- defaultTrustAgent);
- assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
- defaultTrustAgent, newAgentComponentName);
+ assertThat(mEnabledTrustAgents).containsExactly(defaultTrustAgent);
+ assertThat(mKnownTrustAgents).containsExactly(defaultTrustAgent, newAgentComponentName);
}
@Test
@@ -250,9 +291,8 @@
mMockContext.sendPackageChangedBroadcast(newAgentComponentName);
- assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).isEmpty();
- assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
- newAgentComponentName);
+ assertThat(mEnabledTrustAgents).isEmpty();
+ assertThat(mKnownTrustAgents).containsExactly(newAgentComponentName);
}
@Test
@@ -265,50 +305,21 @@
addTrustAgent(systemTrustAgent2, /* isSystemApp= */ true);
bootService();
// Simulate user turning off systemTrustAgent2
- mLockPatternUtils.setEnabledTrustAgents(Collections.singletonList(systemTrustAgent1),
- TEST_USER_ID);
+ mLockPatternUtils.setEnabledTrustAgents(List.of(systemTrustAgent1), TEST_USER_ID);
mMockContext.sendPackageChangedBroadcast(systemTrustAgent2);
- assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
- systemTrustAgent1);
+ assertThat(mEnabledTrustAgents).containsExactly(systemTrustAgent1);
}
@Test
public void reportEnabledTrustAgentsChangedInformsListener() throws RemoteException {
- final LockPatternUtils utils = mock(LockPatternUtils.class);
- final TrustManagerService service = new TrustManagerService(mMockContext,
- new TrustManagerService.Injector(utils, mLooper.getLooper()));
final ITrustListener trustListener = mock(ITrustListener.class);
- final IWindowManager windowManager = mock(IWindowManager.class);
- final int userId = new Random().nextInt();
-
- mMockContext.getTestablePermissions().setPermission(Manifest.permission.TRUST_LISTENER,
- PERMISSION_GRANTED);
-
- when(utils.getKnownTrustAgents(anyInt())).thenReturn(new ArrayList<>());
-
- MockitoSession mockSession = mockitoSession()
- .initMocks(this)
- .mockStatic(ServiceManager.class)
- .mockStatic(WindowManagerGlobal.class)
- .startMocking();
-
- doReturn(windowManager).when(() -> {
- WindowManagerGlobal.getWindowManagerService();
- });
-
- service.onStart();
- ArgumentCaptor<IBinder> binderArgumentCaptor = ArgumentCaptor.forClass(IBinder.class);
- verify(() -> ServiceManager.addService(eq(Context.TRUST_SERVICE),
- binderArgumentCaptor.capture(), anyBoolean(), anyInt()));
- ITrustManager manager = ITrustManager.Stub.asInterface(binderArgumentCaptor.getValue());
- manager.registerTrustListener(trustListener);
- mLooper.dispatchAll();
- manager.reportEnabledTrustAgentsChanged(userId);
- mLooper.dispatchAll();
- verify(trustListener).onEnabledTrustAgentsChanged(eq(userId));
- mockSession.finishMocking();
+ mTrustManager.registerTrustListener(trustListener);
+ mService.waitForIdle();
+ mTrustManager.reportEnabledTrustAgentsChanged(TEST_USER_ID);
+ mService.waitForIdle();
+ verify(trustListener).onEnabledTrustAgentsChanged(TEST_USER_ID);
}
private void addTrustAgent(ComponentName agentComponentName, boolean isSystemApp) {
@@ -327,27 +338,16 @@
mTrustAgentResolveInfoList.add(resolveInfo);
}
- private void initializeEnabledAgents(ComponentName... enabledAgents) {
- mLockPatternUtils.setEnabledTrustAgents(Lists.newArrayList(enabledAgents), TEST_USER_ID);
- Settings.Secure.putIntForUser(mMockContext.getContentResolver(),
- Settings.Secure.TRUST_AGENTS_INITIALIZED, 1, TEST_USER_ID);
- }
-
- private void initializeKnownAgents(ComponentName... knownAgents) {
- mLockPatternUtils.setKnownTrustAgents(Lists.newArrayList(knownAgents), TEST_USER_ID);
- Settings.Secure.putIntForUser(mMockContext.getContentResolver(),
- Settings.Secure.KNOWN_TRUST_AGENTS_INITIALIZED, 1, TEST_USER_ID);
- }
-
private void bootService() {
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ mMockContext.sendUserStartedBroadcast();
}
- private void resetTrustAgentLockSettings() {
- mLockPatternUtils.setEnabledTrustAgents(Collections.emptyList(), TEST_USER_ID);
- mLockPatternUtils.setKnownTrustAgents(Collections.emptyList(), TEST_USER_ID);
+ private void grantPermission(String permission) {
+ mMockContext.getTestablePermissions().setPermission(
+ permission, PackageManager.PERMISSION_GRANTED);
}
/** A mock Context that allows the test process to send protected broadcasts. */
@@ -355,6 +355,8 @@
private final ArrayList<BroadcastReceiver> mPackageChangedBroadcastReceivers =
new ArrayList<>();
+ private final ArrayList<BroadcastReceiver> mUserStartedBroadcastReceivers =
+ new ArrayList<>();
MockContext(Context base) {
super(base);
@@ -369,6 +371,9 @@
if (filter.hasAction(Intent.ACTION_PACKAGE_CHANGED)) {
mPackageChangedBroadcastReceivers.add(receiver);
}
+ if (filter.hasAction(Intent.ACTION_USER_STARTED)) {
+ mUserStartedBroadcastReceivers.add(receiver);
+ }
return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
scheduler);
}
@@ -386,5 +391,13 @@
receiver.onReceive(this, intent);
}
}
+
+ void sendUserStartedBroadcast() {
+ Intent intent = new Intent(Intent.ACTION_USER_STARTED)
+ .putExtra(Intent.EXTRA_USER_HANDLE, TEST_USER_ID);
+ for (BroadcastReceiver receiver : mUserStartedBroadcastReceivers) {
+ receiver.onReceive(this, intent);
+ }
+ }
}
}
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index 91d8ceb..a9ff3a1 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -113,7 +114,8 @@
mTestExecutor.simulateAsyncExecutionOfLastCommand();
// THEN the device vibrates once
- verify(mVibrator, times(1)).vibrate(any(), any(VibrationAttributes.class));
+ verify(mVibrator, times(1)).vibrate(anyInt(), any(), any(), any(),
+ any(VibrationAttributes.class));
}
@Test
@@ -129,7 +131,7 @@
mTestExecutor.simulateAsyncExecutionOfLastCommand();
// THEN the device doesn't vibrate
- verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class));
+ verifyZeroInteractions(mVibrator);
}
@Test
@@ -145,14 +147,15 @@
mTestExecutor.simulateAsyncExecutionOfLastCommand();
// THEN the device vibrates once
- verify(mVibrator, times(1)).vibrate(any(), any(VibrationAttributes.class));
+ verify(mVibrator, times(1)).vibrate(anyInt(), any(), any(), any(),
+ any(VibrationAttributes.class));
}
@Test
public void testVibrateDisabled_wirelessCharging() {
createNotifier();
- // GIVEN the charging vibration is disabeld
+ // GIVEN the charging vibration is disabled
enableChargingVibration(false);
// WHEN wireless charging starts
@@ -161,7 +164,7 @@
mTestExecutor.simulateAsyncExecutionOfLastCommand();
// THEN the device doesn't vibrate
- verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class));
+ verifyZeroInteractions(mVibrator);
}
@Test
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index d752ae4..aec896f 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -97,6 +97,7 @@
import androidx.test.core.app.ApplicationProvider;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -164,6 +165,7 @@
@Mock private AttentionManagerInternal mAttentionManagerInternalMock;
@Mock private DreamManagerInternal mDreamManagerInternalMock;
@Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
+ @Mock private FoldGracePeriodProvider mFoldGracePeriodProvider;
@Mock private Notifier mNotifierMock;
@Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
@Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock;
@@ -359,6 +361,11 @@
DeviceConfigParameterProvider createDeviceConfigParameterProvider() {
return mDeviceParameterProvider;
}
+
+ @Override
+ FoldGracePeriodProvider createFoldGracePeriodProvider() {
+ return mFoldGracePeriodProvider;
+ }
});
return mService;
}
@@ -520,6 +527,8 @@
@Test
public void testWakefulnessSleep_SoftSleepFlag_NoWakelocks() {
+ when(mFoldGracePeriodProvider.isEnabled()).thenReturn(false);
+
createService();
// Start with AWAKE state
startSystem();
@@ -533,6 +542,23 @@
}
@Test
+ public void testWakefulnessAwakeShowKeyguard_SoftSleepFlag_NoWakelocks() {
+ when(mFoldGracePeriodProvider.isEnabled()).thenReturn(true);
+
+ createService();
+ // Start with AWAKE state
+ startSystem();
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+
+ // Take a nap and verify we stay awake and the keyguard is requested
+ mService.getBinderServiceInstance().goToSleep(mClock.now(),
+ PowerManager.GO_TO_SLEEP_REASON_APPLICATION,
+ PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP);
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ verify(mNotifierMock).showDismissibleKeyguard();
+ }
+
+ @Test
public void testWakefulnessSleep_SoftSleepFlag_WithPartialWakelock() {
createService();
// Start with AWAKE state
@@ -548,7 +574,7 @@
null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY,
null /* callback */);
- // Take a nap and verify we stay awake.
+ // Take a nap and verify we go to sleep.
mService.getBinderServiceInstance().goToSleep(mClock.now(),
PowerManager.GO_TO_SLEEP_REASON_APPLICATION,
PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP);
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
index a2f8c8b..6b85a32 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodUtilsTest.java
@@ -25,10 +25,8 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ContentResolver;
@@ -1223,35 +1221,6 @@
StartInputFlags.VIEW_HAS_FOCUS | StartInputFlags.IS_TEXT_EDITOR));
}
- @Test
- public void testInputMethodSettings_SwitchCurrentUser() {
- TestContext ownerUserContext = createMockContext(0 /* userId */);
- final InputMethodInfo systemIme = createFakeInputMethodInfo(
- "SystemIme", "fake.latin", true /* isSystem */);
- final InputMethodInfo nonSystemIme = createFakeInputMethodInfo("NonSystemIme",
- "fake.voice0", false /* isSystem */);
- final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
- methodMap.put(systemIme.getId(), systemIme);
-
- // Init InputMethodSettings for the owner user (userId=0), verify calls can get the
- // corresponding user's context, contentResolver and the resources configuration.
- InputMethodUtils.InputMethodSettings settings = new InputMethodUtils.InputMethodSettings(
- methodMap, 0 /* userId */);
- assertEquals(0, settings.getCurrentUserId());
-
- settings.getEnabledInputMethodSubtypeListLocked(nonSystemIme, true);
- verify(ownerUserContext.getResources(), atLeastOnce()).getConfiguration();
-
- // Calling switchCurrentUser to the secondary user (userId=10), verify calls can get the
- // corresponding user's context, contentResolver and the resources configuration.
- settings.switchCurrentUser(10 /* userId */);
- assertEquals(10, settings.getCurrentUserId());
-
- settings.getEnabledInputMethodSubtypeListLocked(nonSystemIme, true);
- verify(TestContext.getSecondaryUserContext().getResources(),
- atLeastOnce()).getConfiguration();
- }
-
private static IntArray createSubtypeHashCodeArrayFromStr(String subtypeHashCodesStr) {
final IntArray subtypes = new IntArray();
final TextUtils.SimpleStringSplitter imeSubtypeSplitter =
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index f049b33..f5282cb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -143,7 +143,6 @@
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
-import android.util.MergedConfiguration;
import android.util.MutableBoolean;
import android.view.DisplayInfo;
import android.view.IRemoteAnimationFinishedCallback;
@@ -395,8 +394,7 @@
activity.setState(RESUMED, "Testing");
task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
- activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
- activity.getConfiguration()));
+ activity.setLastReportedConfiguration(new Configuration(), activity.getConfiguration());
activity.info.configChanges &= ~CONFIG_ORIENTATION;
final Configuration newConfig = new Configuration(task.getConfiguration());
@@ -420,8 +418,7 @@
activity.setState(RESUMED, "Testing");
task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
- activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
- activity.getConfiguration()));
+ activity.setLastReportedConfiguration(new Configuration(), activity.getConfiguration());
activity.info.configChanges &= ~CONFIG_ORIENTATION;
final Configuration newConfig = new Configuration(task.getConfiguration());
@@ -447,8 +444,7 @@
activity.setState(RESUMED, "Testing");
task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
- activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
- activity.getConfiguration()));
+ activity.setLastReportedConfiguration(new Configuration(), activity.getConfiguration());
activity.info.configChanges &= ~CONFIG_ORIENTATION;
final Configuration newConfig = new Configuration(task.getConfiguration());
@@ -468,8 +464,7 @@
activity.setState(RESUMED, "Testing");
task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
- activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
- activity.getConfiguration()));
+ activity.setLastReportedConfiguration(new Configuration(), activity.getConfiguration());
activity.info.configChanges &= ~ActivityInfo.CONFIG_FONT_SCALE;
final Configuration newConfig = new Configuration(task.getConfiguration());
@@ -571,8 +566,7 @@
.build();
activity.setState(RESUMED, "Testing");
- activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
- activity.getConfiguration()));
+ activity.setLastReportedConfiguration(new Configuration(), activity.getConfiguration());
clearInvocations(mClientLifecycleManager);
@@ -799,8 +793,7 @@
doReturn(false).when(stack).isTranslucent(any());
assertTrue(task.shouldBeVisible(null /* starting */));
- activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
- activity.getConfiguration()));
+ activity.setLastReportedConfiguration(new Configuration(), activity.getConfiguration());
final Configuration newConfig = new Configuration(activity.getConfiguration());
final int shortSide = newConfig.screenWidthDp == newConfig.screenHeightDp
@@ -846,7 +839,7 @@
}
@Test
- public void testTakeOptions() {
+ public void testTakeSceneTransitionInfo() {
final ActivityRecord activity = createActivityWithTask();
ActivityOptions opts = ActivityOptions.makeRemoteAnimation(
new RemoteAnimationAdapter(new Stub() {
@@ -864,7 +857,9 @@
}
}, 0, 0));
activity.updateOptionsLocked(opts);
- assertNotNull(activity.takeOptions());
+ // Ensure the SceneTransitionInfo is null (since the ActivityOptions is for remote
+ // animation and AR#takeSceneTransitionInfo also clear the AR#mPendingOptions
+ assertNull(activity.takeSceneTransitionInfo());
assertNull(activity.getOptions());
final AppTransition appTransition = activity.mDisplayContent.mAppTransition;
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index ac18f80..d83824a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -464,9 +464,10 @@
// Simulate ActivityOptions#makeSceneTransitionAnimation
final Bundle myBundle = new Bundle();
myBundle.putInt(ActivityOptions.KEY_ANIM_TYPE, ANIM_SCENE_TRANSITION);
- myBundle.putParcelable("android:activity.transitionCompleteListener",
- mock(android.os.ResultReceiver.class));
final ActivityOptions options = new ActivityOptions(myBundle);
+ final ActivityOptions.SceneTransitionInfo info = new ActivityOptions.SceneTransitionInfo();
+ info.setResultReceiver(mock(android.os.ResultReceiver.class));
+ options.setSceneTransitionInfo(info);
final ActivityRecord testActivity = new ActivityBuilder(mAtm)
.setCreateTask(true)
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
index dfa595c..99d354a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
@@ -55,6 +55,11 @@
@RunWith(WindowTestRunner.class)
public class DisplayContentDeferredUpdateTests extends WindowTestsBase {
+ // The fields to override the current DisplayInfo.
+ private String mUniqueId;
+ private int mColorMode;
+ private int mLogicalDensityDpi;
+
@Override
protected void onBeforeSystemServicesCreated() {
// Set other flags to their default values
@@ -73,7 +78,7 @@
public void testUpdate_deferrableFieldChangedTransitionStarted_deferrableFieldUpdated() {
performInitialDisplayUpdate();
- givenDisplayInfo(/* uniqueId= */ "old");
+ mUniqueId = "old";
Runnable onUpdated = mock(Runnable.class);
mDisplayContent.requestDisplayUpdate(onUpdated);
@@ -82,11 +87,21 @@
verify(onUpdated).run();
clearInvocations(mDisplayContent.mTransitionController, onUpdated);
- givenDisplayInfo(/* uniqueId= */ "new");
+ mUniqueId = "new";
mDisplayContent.requestDisplayUpdate(onUpdated);
captureStartTransitionCollection().getValue().onCollectStarted(/* deferred= */ true);
verify(onUpdated).run();
+ verify(mDisplayContent.mTransitionController).requestStartTransition(
+ any(), any(), any(), any());
assertThat(mDisplayContent.getDisplayInfo().uniqueId).isEqualTo("new");
+ clearInvocations(mDisplayContent.mTransitionController, onUpdated);
+
+ mLogicalDensityDpi += 100;
+ mDisplayContent.requestDisplayUpdate(onUpdated);
+ captureStartTransitionCollection().getValue().onCollectStarted(/* deferred= */ true);
+ verify(onUpdated).run();
+ verify(mDisplayContent.mTransitionController).requestStartTransition(
+ any(), any(), any(), any());
}
@Test
@@ -94,7 +109,8 @@
performInitialDisplayUpdate();
// Update only color mode (non-deferrable field) and keep the same unique id
- givenDisplayInfo(/* uniqueId= */ "initial_unique_id", /* colorMode= */ 123);
+ mUniqueId = "initial_unique_id";
+ mColorMode = 123;
Runnable onUpdated = mock(Runnable.class);
mDisplayContent.requestDisplayUpdate(onUpdated);
@@ -107,7 +123,8 @@
performInitialDisplayUpdate();
// Update only color mode (non-deferrable field) and keep the same unique id
- givenDisplayInfo(/* uniqueId= */ "initial_unique_id", /* colorMode= */ 123);
+ mUniqueId = "initial_unique_id";
+ mColorMode = 123;
mDisplayContent.requestDisplayUpdate(mock(Runnable.class));
assertThat(mDisplayContent.getDisplayInfo().colorMode).isEqualTo(123);
@@ -116,7 +133,7 @@
// Update unique id (deferrable field), keep the same color mode,
// this update should be deferred
- givenDisplayInfo(/* uniqueId= */ "new_unique_id", /* colorMode= */ 123);
+ mUniqueId = "new_unique_id";
mDisplayContent.requestDisplayUpdate(mock(Runnable.class));
assertThat(mDisplayContent.getDisplayInfo().colorMode).isEqualTo(123);
@@ -126,7 +143,7 @@
// Update color mode again and keep the same unique id, color mode update
// should not be deferred, unique id update is still deferred as transition
// has not started collecting yet
- givenDisplayInfo(/* uniqueId= */ "new_unique_id", /* colorMode= */ 456);
+ mColorMode = 456;
Runnable onUpdated = mock(Runnable.class);
mDisplayContent.requestDisplayUpdate(onUpdated);
@@ -146,14 +163,14 @@
@Test
public void testUpdate_deferrableFieldUpdatedTransitionPending_fieldNotUpdated() {
performInitialDisplayUpdate();
- givenDisplayInfo(/* uniqueId= */ "old");
+ mUniqueId = "old";
Runnable onUpdated = mock(Runnable.class);
mDisplayContent.requestDisplayUpdate(onUpdated);
captureStartTransitionCollection().getValue().onCollectStarted(/* deferred= */ true);
verify(onUpdated).run();
clearInvocations(mDisplayContent.mTransitionController, onUpdated);
- givenDisplayInfo(/* uniqueId= */ "new");
+ mUniqueId = "new";
mDisplayContent.requestDisplayUpdate(onUpdated);
captureStartTransitionCollection(); // do not continue by not starting the collection
@@ -164,7 +181,7 @@
@Test
public void testTwoDisplayUpdates_transitionStarted_displayUpdated() {
performInitialDisplayUpdate();
- givenDisplayInfo(/* uniqueId= */ "old");
+ mUniqueId = "old";
Runnable onUpdated = mock(Runnable.class);
mDisplayContent.requestDisplayUpdate(onUpdated);
captureStartTransitionCollection().getValue()
@@ -173,10 +190,10 @@
clearInvocations(mDisplayContent.mTransitionController, onUpdated);
// Perform two display updates while WM is 'busy'
- givenDisplayInfo(/* uniqueId= */ "new1");
+ mUniqueId = "new1";
Runnable onUpdated1 = mock(Runnable.class);
mDisplayContent.requestDisplayUpdate(onUpdated1);
- givenDisplayInfo(/* uniqueId= */ "new2");
+ mUniqueId = "new2";
Runnable onUpdated2 = mock(Runnable.class);
mDisplayContent.requestDisplayUpdate(onUpdated2);
@@ -215,22 +232,19 @@
return callbackCaptor;
}
- private void givenDisplayInfo(String uniqueId) {
- givenDisplayInfo(uniqueId, /* colorMode= */ 0);
- }
+ private void performInitialDisplayUpdate() {
+ mUniqueId = "initial_unique_id";
+ mColorMode = 0;
+ mLogicalDensityDpi = 400;
- private void givenDisplayInfo(String uniqueId, int colorMode) {
spyOn(mDisplayContent.mDisplay);
doAnswer(invocation -> {
DisplayInfo info = invocation.getArgument(0);
- info.uniqueId = uniqueId;
- info.colorMode = colorMode;
+ info.uniqueId = mUniqueId;
+ info.colorMode = mColorMode;
+ info.logicalDensityDpi = mLogicalDensityDpi;
return null;
}).when(mDisplayContent.mDisplay).getDisplayInfo(any());
- }
-
- private void performInitialDisplayUpdate() {
- givenDisplayInfo(/* uniqueId= */ "initial_unique_id", /* colorMode= */ 0);
Runnable onUpdated = mock(Runnable.class);
mDisplayContent.requestDisplayUpdate(onUpdated);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 28fecd6..6013063 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -76,7 +76,6 @@
import android.os.PowerManager;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
-import android.util.MergedConfiguration;
import android.util.Pair;
import androidx.test.filters.MediumTest;
@@ -545,8 +544,7 @@
assertNotEquals(activity.getConfiguration().orientation, rotatedConfig.orientation);
// Assume the activity was shown in different orientation. For example, the top activity is
// landscape and the portrait lockscreen is shown.
- activity.setLastReportedConfiguration(
- new MergedConfiguration(mAtm.getGlobalConfiguration(), rotatedConfig));
+ activity.setLastReportedConfiguration(mAtm.getGlobalConfiguration(), rotatedConfig);
activity.setState(STOPPED, "sleep");
display.setIsSleeping(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
index 810cbe8..be30593 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
@@ -134,6 +134,14 @@
assertFalse(r.isSyncFinished(r.getSyncGroup()));
r.finishRelaunching();
assertTrue(r.isSyncFinished(r.getSyncGroup()));
+ assertEquals(SYNC_STATE_READY, r.mSyncState);
+
+ // If the container has finished the sync, isSyncFinished should not change the sync state.
+ final BLASTSyncEngine.SyncGroup syncGroup = r.getSyncGroup();
+ r.finishSync(mTransaction, syncGroup, false /* cancel */);
+ assertEquals(SYNC_STATE_NONE, r.mSyncState);
+ assertTrue(r.isSyncFinished(syncGroup));
+ assertEquals(SYNC_STATE_NONE, r.mSyncState);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 52e2d8a..7551b165 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -330,6 +330,10 @@
}
@Override
+ public void showDismissibleKeyguard() {
+ }
+
+ @Override
public void setPipVisibilityLw(boolean visible) {
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 8bf4833..21fee72 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -37,6 +37,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -85,6 +86,7 @@
import android.view.InputChannel;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowInsets;
@@ -952,6 +954,57 @@
}
@Test
+ public void testDrawMagnifiedViewport() {
+ final int displayId = mDisplayContent.mDisplayId;
+ // Use real surface, so ViewportWindow's BlastBufferQueue can be created.
+ final ArrayList<SurfaceControl> surfaceControls = new ArrayList<>();
+ mWm.mSurfaceControlFactory = s -> new SurfaceControl.Builder() {
+ @Override
+ public SurfaceControl build() {
+ final SurfaceControl sc = super.build();
+ surfaceControls.add(sc);
+ return sc;
+ }
+ };
+ mWm.mAccessibilityController.setMagnificationCallbacks(displayId,
+ mock(WindowManagerInternal.MagnificationCallbacks.class));
+ final boolean[] lockCanvasInWmLock = { false };
+ final Surface surface = mWm.mAccessibilityController.forceShowMagnifierSurface(displayId);
+ spyOn(surface);
+ doAnswer(invocationOnMock -> {
+ lockCanvasInWmLock[0] |= Thread.holdsLock(mWm.mGlobalLock);
+ invocationOnMock.callRealMethod();
+ return null;
+ }).when(surface).lockCanvas(any());
+ mWm.mAccessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId, mTransaction);
+ waitUntilHandlersIdle();
+ try {
+ verify(surface).lockCanvas(any());
+
+ clearInvocations(surface);
+ // Invalidate and redraw.
+ mWm.mAccessibilityController.onDisplaySizeChanged(mDisplayContent);
+ mWm.mAccessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId, mTransaction);
+ // Turn off magnification to release surface.
+ mWm.mAccessibilityController.setMagnificationCallbacks(displayId, null);
+ if (!com.android.window.flags.Flags.drawMagnifierBorderOutsideWmlock()) {
+ verify(surface).release();
+ assertTrue(lockCanvasInWmLock[0]);
+ return;
+ }
+ waitUntilHandlersIdle();
+ // lockCanvas must not be called after releasing.
+ verify(surface, never()).lockCanvas(any());
+ verify(surface).release();
+ assertFalse(lockCanvasInWmLock[0]);
+ } finally {
+ for (int i = surfaceControls.size() - 1; i >= 0; --i) {
+ surfaceControls.get(i).release();
+ }
+ }
+ }
+
+ @Test
public void testRequestKeyboardShortcuts_noWindow() {
doNothing().when(mWm.mContext).enforceCallingOrSelfPermission(anyString(), anyString());
doReturn(null).when(mWm).getFocusedWindowLocked();
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index d05eb5c..57b13e9 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -422,8 +422,8 @@
"android.telecom.extra.CALL_CREATED_TIME_MILLIS";
/**
- * The extra for call log uri that was used to mark missed calls as read when dialer gets the
- * notification on reboot.
+ * Extra URI that is used by a dialer to query the {@link android.provider.CallLog} content
+ * provider and associate a missed call notification with a call log entry.
*/
@FlaggedApi(Flags.FLAG_ADD_CALL_URI_FOR_MISSED_CALLS)
public static final String EXTRA_CALL_LOG_URI =
diff --git a/telephony/java/android/telephony/SecurityAlgorithmUpdate.java b/telephony/java/android/telephony/SecurityAlgorithmUpdate.java
index 61d7ead..c902016 100644
--- a/telephony/java/android/telephony/SecurityAlgorithmUpdate.java
+++ b/telephony/java/android/telephony/SecurityAlgorithmUpdate.java
@@ -169,6 +169,7 @@
public static final int SECURITY_ALGORITHM_NEA1 = 56;
public static final int SECURITY_ALGORITHM_NEA2 = 57;
public static final int SECURITY_ALGORITHM_NEA3 = 58;
+ public static final int SECURITY_ALGORITHM_IMS_NULL = 67;
public static final int SECURITY_ALGORITHM_SIP_NULL = 68;
public static final int SECURITY_ALGORITHM_AES_GCM = 69;
public static final int SECURITY_ALGORITHM_AES_GMAC = 70;
@@ -176,9 +177,8 @@
public static final int SECURITY_ALGORITHM_DES_EDE3_CBC = 72;
public static final int SECURITY_ALGORITHM_AES_EDE3_CBC = 73;
public static final int SECURITY_ALGORITHM_HMAC_SHA1_96 = 74;
- public static final int SECURITY_ALGORITHM_HMAC_SHA1_96_NULL = 75;
- public static final int SECURITY_ALGORITHM_HMAC_MD5_96 = 76;
- public static final int SECURITY_ALGORITHM_HMAC_MD5_96_NULL = 77;
+ public static final int SECURITY_ALGORITHM_HMAC_MD5_96 = 75;
+ public static final int SECURITY_ALGORITHM_SRTP_NULL = 86;
public static final int SECURITY_ALGORITHM_SRTP_AES_COUNTER = 87;
public static final int SECURITY_ALGORITHM_SRTP_AES_F8 = 88;
public static final int SECURITY_ALGORITHM_SRTP_HMAC_SHA1 = 89;
@@ -199,15 +199,15 @@
SECURITY_ALGORITHM_UEA2, SECURITY_ALGORITHM_EEA0, SECURITY_ALGORITHM_EEA1,
SECURITY_ALGORITHM_EEA2, SECURITY_ALGORITHM_EEA3, SECURITY_ALGORITHM_NEA0,
SECURITY_ALGORITHM_NEA1, SECURITY_ALGORITHM_NEA2, SECURITY_ALGORITHM_NEA3,
- SECURITY_ALGORITHM_SIP_NULL, SECURITY_ALGORITHM_AES_GCM,
+ SECURITY_ALGORITHM_IMS_NULL, SECURITY_ALGORITHM_SIP_NULL, SECURITY_ALGORITHM_AES_GCM,
SECURITY_ALGORITHM_AES_GMAC, SECURITY_ALGORITHM_AES_CBC,
SECURITY_ALGORITHM_DES_EDE3_CBC, SECURITY_ALGORITHM_AES_EDE3_CBC,
- SECURITY_ALGORITHM_HMAC_SHA1_96, SECURITY_ALGORITHM_HMAC_SHA1_96_NULL,
- SECURITY_ALGORITHM_HMAC_MD5_96, SECURITY_ALGORITHM_HMAC_MD5_96_NULL,
- SECURITY_ALGORITHM_SRTP_AES_COUNTER, SECURITY_ALGORITHM_SRTP_AES_F8,
- SECURITY_ALGORITHM_SRTP_HMAC_SHA1, SECURITY_ALGORITHM_ENCR_AES_GCM_16,
- SECURITY_ALGORITHM_ENCR_AES_CBC, SECURITY_ALGORITHM_AUTH_HMAC_SHA2_256_128,
- SECURITY_ALGORITHM_UNKNOWN, SECURITY_ALGORITHM_OTHER, SECURITY_ALGORITHM_ORYX})
+ SECURITY_ALGORITHM_HMAC_SHA1_96, SECURITY_ALGORITHM_HMAC_MD5_96,
+ SECURITY_ALGORITHM_SRTP_NULL, SECURITY_ALGORITHM_SRTP_AES_COUNTER,
+ SECURITY_ALGORITHM_SRTP_AES_F8, SECURITY_ALGORITHM_SRTP_HMAC_SHA1,
+ SECURITY_ALGORITHM_ENCR_AES_GCM_16, SECURITY_ALGORITHM_ENCR_AES_CBC,
+ SECURITY_ALGORITHM_AUTH_HMAC_SHA2_256_128, SECURITY_ALGORITHM_UNKNOWN,
+ SECURITY_ALGORITHM_OTHER, SECURITY_ALGORITHM_ORYX})
public @interface SecurityAlgorithm {
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 9e292be..1b47dfe 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -510,7 +510,7 @@
/** @hide */
@UnsupportedAppUsage
public TelephonyManager(Context context) {
- this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
+ this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
}
/** @hide */
@@ -2140,10 +2140,14 @@
* the IMEI/SV for GSM phones. Return null if the software version is
* not available.
* <p>
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY}.
*/
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PHONE_STATE,
android.Manifest.permission.READ_BASIC_PHONE_STATE})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
@Nullable
public String getDeviceSoftwareVersion() {
return getDeviceSoftwareVersion(getSlotIndex());
@@ -2158,10 +2162,13 @@
*
* @param slotIndex of which deviceID is returned
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY}.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
@Nullable
public String getDeviceSoftwareVersion(int slotIndex) {
ITelephony telephony = getITelephony();
@@ -2288,6 +2295,9 @@
*
* See {@link #getImei(int)} for details on the required permissions and behavior
* when the caller does not hold sufficient permissions.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_GSM}.
*/
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -2330,6 +2340,9 @@
* </ul>
*
* @param slotIndex of which IMEI is returned
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_GSM}.
*/
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -2350,6 +2363,9 @@
/**
* Returns the Type Allocation Code from the IMEI. Return null if Type Allocation Code is not
* available.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_GSM}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
@Nullable
@@ -2362,6 +2378,9 @@
* available.
*
* @param slotIndex of which Type Allocation Code is returned
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_GSM}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
@Nullable
@@ -2407,6 +2426,9 @@
* the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
* higher, then a SecurityException is thrown.</li>
* </ul>
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
*/
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -2446,6 +2468,9 @@
* </ul>
*
* @param slotIndex of which MEID is returned
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
*/
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -2472,6 +2497,9 @@
/**
* Returns the Manufacturer Code from the MEID. Return null if Manufacturer Code is not
* available.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@Nullable
@@ -2484,6 +2512,9 @@
* available.
*
* @param slotIndex of which Type Allocation Code is returned
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@Nullable
@@ -2528,6 +2559,9 @@
* the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
* higher, then a SecurityException is thrown.</li>
* </ul>
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -2563,10 +2597,14 @@
*<p>
* @return Current location of the device or null if not available.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+ *
* @deprecated use {@link #getAllCellInfo} instead, which returns a superset of this API.
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public CellLocation getCellLocation() {
try {
ITelephony telephony = getITelephony();
@@ -2596,12 +2634,15 @@
*
* @return List of NeighboringCellInfo or null if info unavailable.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @removed
* @deprecated Use {@link #getAllCellInfo} which returns a superset of the information
* from NeighboringCellInfo, including LTE cell information.
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public List<NeighboringCellInfo> getNeighboringCellInfo() {
try {
ITelephony telephony = getITelephony();
@@ -2648,9 +2689,12 @@
* @see #PHONE_TYPE_CDMA
* @see #PHONE_TYPE_SIP
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY}.
* {@hide}
*/
@SystemApi
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
public int getCurrentPhoneType() {
return getCurrentPhoneType(getSubId());
}
@@ -2663,9 +2707,13 @@
* @see #PHONE_TYPE_CDMA
*
* @param subId for which phone type is returned
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY}.
* @hide
*/
@SystemApi
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
public int getCurrentPhoneType(int subId) {
int phoneId;
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
@@ -2712,7 +2760,11 @@
* @see #PHONE_TYPE_GSM
* @see #PHONE_TYPE_CDMA
* @see #PHONE_TYPE_SIP
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY}.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
public int getPhoneType() {
if (!isVoiceCapable()) {
return PHONE_TYPE_NONE;
@@ -2912,6 +2964,9 @@
* @see CarrierConfigManager#getConfigForSubId(int)
* @see #createForSubscriptionId(int)
* @see #createForPhoneAccountHandle(PhoneAccountHandle)
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@WorkerThread
@@ -2958,6 +3013,9 @@
* <p>
* @return the lowercase 2 character ISO-3166-1 alpha-2 country code, or empty string if not
* available.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public String getNetworkCountryIso() {
@@ -2980,6 +3038,8 @@
* available.
*
* @throws IllegalArgumentException when the slotIndex is invalid.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -3108,9 +3168,13 @@
*
* @deprecated use {@link #getDataNetworkType()}
* @return the NETWORK_TYPE_xxxx for current data connection.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public @NetworkType int getNetworkType() {
return getNetworkType(getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
}
@@ -3199,12 +3263,15 @@
* @see #NETWORK_TYPE_EHRPD
* @see #NETWORK_TYPE_HSPAP
* @see #NETWORK_TYPE_NR
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PHONE_STATE,
android.Manifest.permission.READ_BASIC_PHONE_STATE})
- @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public @NetworkType int getDataNetworkType() {
return getDataNetworkType(getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
}
@@ -3245,6 +3312,9 @@
* or {@link android.Manifest.permission#READ_BASIC_PHONE_STATE
* READ_BASIC_PHONE_STATE} or that the calling app has carrier privileges
* (see {@link #hasCarrierPrivileges}).
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(anyOf = {
@@ -3597,6 +3667,9 @@
* of whether an active SIM profile is present or not so this API would always return true.
*
* @return true if a ICC card is present.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public boolean hasIccCard() {
@@ -3640,6 +3713,9 @@
* @see #SIM_STATE_PERM_DISABLED
* @see #SIM_STATE_CARD_IO_ERROR
* @see #SIM_STATE_CARD_RESTRICTED
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @SimState int getSimState() {
@@ -3681,6 +3757,8 @@
* @see #SIM_STATE_CARD_RESTRICTED
* @see #SIM_STATE_PRESENT
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@@ -3701,11 +3779,14 @@
* @see #SIM_STATE_CARD_RESTRICTED
* @see #SIM_STATE_PRESENT
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
* @deprecated instead use {@link #getSimCardState(int, int)}
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@Deprecated
public @SimState int getSimCardState(int physicalSlotIndex) {
int activePort = getFirstActivePortIndex(physicalSlotIndex);
@@ -3727,6 +3808,8 @@
* @see #SIM_STATE_CARD_RESTRICTED
* @see #SIM_STATE_PRESENT
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@@ -3785,6 +3868,8 @@
* @see #SIM_STATE_PERM_DISABLED
* @see #SIM_STATE_LOADED
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@@ -3808,11 +3893,14 @@
* @see #SIM_STATE_PERM_DISABLED
* @see #SIM_STATE_LOADED
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
* @deprecated instead use {@link #getSimApplicationState(int, int)}
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@Deprecated
public @SimState int getSimApplicationState(int physicalSlotIndex) {
int activePort = getFirstActivePortIndex(physicalSlotIndex);
@@ -3836,6 +3924,8 @@
* @see #SIM_STATE_PERM_DISABLED
* @see #SIM_STATE_LOADED
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@@ -3876,6 +3966,9 @@
*
* @param appType the uicc app type like {@link APPTYPE_CSIM}
* @return true if the specified type of application in UICC CARD or false if no uicc or error.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@@ -3908,6 +4001,9 @@
* @see #SIM_STATE_PERM_DISABLED
* @see #SIM_STATE_CARD_IO_ERROR
* @see #SIM_STATE_CARD_RESTRICTED
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @SimState int getSimState(int slotIndex) {
@@ -4105,6 +4201,9 @@
* the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
* higher, then a SecurityException is thrown.</li>
* </ul>
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -4172,6 +4271,9 @@
*
* @return {@code true} if 3GPP and 3GPP2 radio technologies can be supported at the same time
* {@code false} if not supported or unknown
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@SystemApi
@@ -4219,6 +4321,9 @@
* through a factory reset.
*
* @return card ID of the default eUICC card, if loaded.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
public int getCardIdForDefaultEuicc() {
@@ -4252,6 +4357,9 @@
* @return a list of UiccCardInfo objects, representing information on the currently inserted
* UICCs and eUICCs. Each UiccCardInfo in the list will have private information filtered out if
* the caller does not have adequate permissions for that card.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -4276,6 +4384,8 @@
*
* @return UiccSlotInfo array.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@@ -4319,6 +4429,9 @@
* @param physicalSlots The content of the array represents the physical slot index. The array
* size should be same as {@link #getUiccSlotsInfo()}.
* @return boolean Return true if the switch succeeds, false if the switch fails.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
* @deprecated {@link #setSimSlotMapping(Collection, Executor, Consumer)}
*/
@@ -4328,6 +4441,7 @@
@SystemApi
@Deprecated
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public boolean switchSlots(int[] physicalSlots) {
try {
ITelephony telephony = getITelephony();
@@ -4420,6 +4534,8 @@
* @throws IllegalArgumentException if the caller passes in an invalid collection of
* UiccSlotMapping like duplicate data, etc
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@@ -4452,11 +4568,14 @@
* @return a map indicates the mapping from logical slots to physical slots. The size of the map
* should be {@link #getPhoneCount()} if success, otherwise return an empty map.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
* @deprecated use {@link #getSimSlotMapping()} instead.
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@NonNull
@Deprecated
public Map<Integer, Integer> getLogicalToPhysicalSlotMapping() {
@@ -4484,6 +4603,9 @@
*
* @return a collection of {@link UiccSlotMapping} which indicates the mapping from logical
* slots to ports and physical slots.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@@ -4541,6 +4663,9 @@
* the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
* higher, then a SecurityException is thrown.</li>
* </ul>
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -4593,6 +4718,8 @@
* found, and the carrier does not require a key.
* @throws IllegalArgumentException when an invalid key is found or when key is required but
* not found.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -4638,6 +4765,9 @@
* Requires Permission: MODIFY_PHONE_STATE.
*
* @see #getCarrierInfoForImsiEncryption
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -4840,6 +4970,9 @@
* from disk, as well as on which {@code callback} will be called.
* @param callback A callback called when the upload operation terminates, either in success
* or in error.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void uploadCallComposerPicture(@NonNull Path pictureToUpload,
@@ -4947,6 +5080,9 @@
* read, as well as on which the callback will be called.
* @param callback A callback called when the upload operation terminates, either in success
* or in error.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void uploadCallComposerPicture(@NonNull InputStream pictureToUpload,
@@ -5081,6 +5217,9 @@
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -5139,6 +5278,8 @@
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* for apps targeting SDK API level 29 and below.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @deprecated use {@link SubscriptionManager#getPhoneNumber(int)} instead.
*/
@Deprecated
@@ -5148,6 +5289,7 @@
android.Manifest.permission.READ_SMS,
android.Manifest.permission.READ_PHONE_NUMBERS
})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public String getLine1Number() {
return getLine1Number(getSubId());
}
@@ -5214,9 +5356,13 @@
* @param alphaTag alpha-tagging of the dailing nubmer
* @param number The dialing number
* @return true if the operation was executed correctly.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @deprecated use {@link SubscriptionManager#setCarrierPhoneNumber(int, String)} instead.
*/
@Deprecated
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public boolean setLine1NumberForDisplay(String alphaTag, String number) {
return setLine1NumberForDisplay(getSubId(), alphaTag, number);
}
@@ -5336,6 +5482,8 @@
* {@link SubscriptionManager#createSubscriptionGroup(List)} for the definition of a group,
* otherwise return an empty array if there is a failure.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@@ -5421,6 +5569,9 @@
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -5459,6 +5610,9 @@
*
* @param alphaTag The alpha tag to display.
* @param number The voicemail number.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public boolean setVoiceMailNumber(String alphaTag, String number) {
@@ -5533,6 +5687,8 @@
* @see #KEY_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL
* @see #KEY_VOICEMAIL_SCRAMBLED_PIN_STRING
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
* @hide
*/
@SystemApi
@@ -5563,6 +5719,9 @@
* @see #createForSubscriptionId(int)
* @see #createForPhoneAccountHandle(PhoneAccountHandle)
* @see VisualVoicemailService
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@Nullable
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@@ -5593,6 +5752,9 @@
*
* @see TelecomManager#getDefaultDialerPackage()
* @see CarrierConfigManager#KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void setVisualVoicemailSmsFilterSettings(VisualVoicemailSmsFilterSettings settings) {
@@ -5623,6 +5785,9 @@
*
* @see SmsManager#sendDataMessage(String, String, short, byte[], PendingIntent, PendingIntent)
* @see SmsManager#sendTextMessage(String, String, String, PendingIntent, PendingIntent)
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void sendVisualVoicemailSms(String number, int port, String text,
@@ -5808,6 +5973,9 @@
* @see #SIM_ACTIVATION_STATE_ACTIVATING
* @see #SIM_ACTIVATION_STATE_ACTIVATED
* @see #SIM_ACTIVATION_STATE_DEACTIVATED
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}
* @hide
*/
@SystemApi
@@ -5856,6 +6024,9 @@
* @see #SIM_ACTIVATION_STATE_ACTIVATED
* @see #SIM_ACTIVATION_STATE_DEACTIVATED
* @see #SIM_ACTIVATION_STATE_RESTRICTED
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
* @hide
*/
@SystemApi
@@ -5904,6 +6075,9 @@
* @see #SIM_ACTIVATION_STATE_ACTIVATING
* @see #SIM_ACTIVATION_STATE_ACTIVATED
* @see #SIM_ACTIVATION_STATE_DEACTIVATED
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
* @hide
*/
@SystemApi
@@ -5954,6 +6128,9 @@
* @see #SIM_ACTIVATION_STATE_ACTIVATED
* @see #SIM_ACTIVATION_STATE_DEACTIVATED
* @see #SIM_ACTIVATION_STATE_RESTRICTED
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
* @hide
*/
@SystemApi
@@ -6032,6 +6209,9 @@
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -6072,7 +6252,10 @@
*
* @throws SecurityException if the caller does not have carrier privileges or is not the
* current default dialer
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void sendDialerSpecialCode(String inputCode) {
try {
final ITelephony telephony = getITelephony();
@@ -6143,6 +6326,9 @@
*
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@Nullable
@@ -6168,6 +6354,9 @@
* Returns the IMS public user identities (IMPU) that were loaded from the ISIM.
* @return an array of IMPU strings, with one IMPU per string, or null if
* not present or not loaded
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
* @deprecated use {@link #getImsPublicUserIdentities()}
*/
@@ -6199,6 +6388,8 @@
* EF_IMPU is not available.
* @throws IllegalStateException in case the ISIM hasn’t been loaded
* @throws SecurityException if the caller does not have the required permission/privilege
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@NonNull
@@ -6254,6 +6445,10 @@
* targeting API level 31+.
*
* @return the current call state.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELECOM}.
+ *
* @deprecated Use {@link #getCallStateForSubscription} to retrieve the call state for a
* specific telephony subscription (which allows carrier privileged apps),
* {@link TelephonyCallback.CallStateListener} for real-time call state updates, or
@@ -6261,6 +6456,7 @@
* device.
*/
@RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true)
+ @RequiresFeature(PackageManager.FEATURE_TELECOM)
@Deprecated
public @CallState int getCallState() {
if (mContext != null) {
@@ -6281,6 +6477,9 @@
* @see TelephonyManager#createForSubscriptionId(int)
* @see TelephonyManager#createForPhoneAccountHandle(PhoneAccountHandle)
* @return The call state of the subscription associated with this TelephonyManager instance.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
@@ -6341,6 +6540,9 @@
* @see #DATA_ACTIVITY_OUT
* @see #DATA_ACTIVITY_INOUT
* @see #DATA_ACTIVITY_DORMANT
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public int getDataActivity() {
@@ -6414,6 +6616,9 @@
* @see #DATA_SUSPENDED
* @see #DATA_DISCONNECTING
* @see #DATA_HANDOVER_IN_PROGRESS
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public int getDataState() {
@@ -6599,10 +6804,14 @@
* Returns the CDMA ERI icon display number. The number is assigned by
* 3GPP2 C.R1001-H v1.0 Table 8.1-1. Additionally carriers define their own ERI display numbers.
* Defined values are {@link #ERI_ON}, {@link #ERI_OFF}, and {@link #ERI_FLASH}.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
public @EriIconIndex int getCdmaEnhancedRoamingIndicatorDisplayNumber() {
return getCdmaEriIconIndex(getSubId());
}
@@ -6810,6 +7019,9 @@
*
* @return List of {@link android.telephony.CellInfo}; null if cell
* information is unavailable.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -6905,6 +7117,9 @@
*
* @param executor the executor on which callback will be invoked.
* @param callback a callback to receive CellInfo.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -6966,6 +7181,9 @@
* @param workSource the requestor to whom the power consumption for this should be attributed.
* @param executor the executor on which callback will be invoked.
* @param callback a callback to receive CellInfo.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@SystemApi
@@ -7052,6 +7270,9 @@
/**
* Returns the MMS user agent.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
public String getMmsUserAgent() {
@@ -7068,6 +7289,9 @@
/**
* Returns the MMS user agent profile URL.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
public String getMmsUAProfUrl() {
@@ -7111,8 +7335,12 @@
*
* @param AID Application id. See ETSI 102.221 and 101.220.
* @return an IccOpenLogicalChannelResponse object.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @deprecated Replaced by {@link #iccOpenLogicalChannel(String, int)}
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@Deprecated
public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID) {
return iccOpenLogicalChannel(getSubId(), AID, -1);
@@ -7145,6 +7373,8 @@
* @param aid Application id. See ETSI 102.221 and 101.220.
* @param p2 P2 parameter (described in ISO 7816-4).
* @return an IccOpenLogicalChannelResponse object.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
* @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
* instead use {@link #iccOpenLogicalChannelByPort(int, int, String, int)}
@@ -7200,9 +7430,13 @@
* @param aid Application id. See ETSI 102.221 and 101.220.
* @param p2 P2 parameter (described in ISO 7816-4).
* @return an IccOpenLogicalChannelResponse object.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@SystemApi
@NonNull
public IccOpenLogicalChannelResponse iccOpenLogicalChannelByPort(int slotIndex,
@@ -7255,6 +7489,9 @@
* @param AID Application id. See ETSI 102.221 and 101.220.
* @param p2 P2 parameter (described in ISO 7816-4).
* @return an IccOpenLogicalChannelResponse object.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID, int p2) {
@@ -7321,6 +7558,9 @@
* @param channel is the channel id to be closed as returned by a successful
* iccOpenLogicalChannel.
* @return true if the channel was closed successfully.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
* @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
* instead use {@link #iccCloseLogicalChannelByPort(int, int, int)}
@@ -7365,9 +7605,12 @@
* @throws IllegalStateException if the Telephony process is not currently available or modem
* currently can't process this command.
* @throws IllegalArgumentException if invalid arguments are passed.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@SystemApi
public void iccCloseLogicalChannelByPort(int slotIndex, int portIndex, int channel) {
try {
@@ -7403,6 +7646,8 @@
* iccOpenLogicalChannel.
* @return true if the channel was closed successfully.
* @throws IllegalArgumentException if input parameters are wrong. e.g., invalid channel
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public boolean iccCloseLogicalChannel(int channel) {
@@ -7469,6 +7714,8 @@
* @param data Data to be sent with the APDU.
* @return The APDU response from the ICC card with the status appended at the end, or null if
* there is an issue connecting to the Telephony service.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
* @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
* instead use
@@ -7516,9 +7763,13 @@
* @param data Data to be sent with the APDU.
* @return The APDU response from the ICC card with the status appended at the end, or null if
* there is an issue connecting to the Telephony service.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@SystemApi
@NonNull
public String iccTransmitApduLogicalChannelByPort(int slotIndex, int portIndex, int channel,
@@ -7563,6 +7814,9 @@
* @param data Data to be sent with the APDU.
* @return The APDU response from the ICC card with the status appended at
* the end.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public String iccTransmitApduLogicalChannel(int channel, int cla,
@@ -7628,6 +7882,9 @@
* @param data Data to be sent with the APDU.
* @return The APDU response from the ICC card with the status appended at
* the end.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
* @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
* instead use
@@ -7673,9 +7930,13 @@
* @param data Data to be sent with the APDU.
* @return The APDU response from the ICC card with the status appended at
* the end.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@SystemApi
@NonNull
public String iccTransmitApduBasicChannelByPort(int slotIndex, int portIndex, int cla,
@@ -7712,6 +7973,9 @@
* @param data Data to be sent with the APDU.
* @return The APDU response from the ICC card with the status appended at
* the end.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public String iccTransmitApduBasicChannel(int cla,
@@ -7768,6 +8032,9 @@
* @param p3 P3 value of the APDU command.
* @param filePath
* @return The APDU response.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public byte[] iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3,
@@ -7817,6 +8084,9 @@
* @return The APDU response from the ICC card in hexadecimal format
* with the last 4 bytes being the status word. If the command fails,
* returns an empty string.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public String sendEnvelopeWithStatus(String content) {
@@ -7978,6 +8248,8 @@
*
* @return {@code true} on success; {@code false} on any failure.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -8008,6 +8280,8 @@
*
* @deprecated Using {@link #rebootModem()} instead.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -8035,6 +8309,8 @@
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
* @throws IllegalStateException if the Telephony process is not currently available.
* @throws RuntimeException
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -8166,6 +8442,9 @@
* {@link #getMaxNumberVerificationTimeoutMillis()}, whichever is lesser.
* @param executor The {@link Executor} that callbacks should be executed on.
* @param callback The callback to use for delivering results.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -8378,6 +8657,9 @@
* See 3GPP TS 31.103 (Section 4.2.7) for the definition and more information on this table.
*
* @return IMS Service Table or null if not present or not loaded
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@Nullable
@@ -8496,6 +8778,9 @@
* Key freshness failure
* Authentication error, no memory space available
* Authentication error, no memory space available in EFMUK
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
// TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not
// READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since
@@ -8552,6 +8837,9 @@
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
* @return an array of forbidden PLMNs or null if not available
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -8602,6 +8890,9 @@
* @return number of PLMNs that were successfully written to the SIM FPLMN list.
* This may be less than the number of PLMNs passed in where the SIM file does not have enough
* room for all of the values passed in. Return -1 in the event of an unexpected failure
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -8637,6 +8928,8 @@
* @param appType of type int of either {@link #APPTYPE_USIM} or {@link #APPTYPE_ISIM}.
* @return HexString represents sim service table else null.
* @throws SecurityException if the caller does not have the required permission/privileges
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@@ -8673,6 +8966,9 @@
* state.
*
* @param slotIndex the sim slot to reset the IMS stack on.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_IMS}.
* @hide */
@SystemApi
@WorkerThread
@@ -9084,12 +9380,15 @@
*
* @return The bitmask of preferred network types.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
* @deprecated Use {@link #getAllowedNetworkTypesBitmask} instead.
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@SystemApi
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public @NetworkTypeBitMask long getPreferredNetworkTypeBitmask() {
return getAllowedNetworkTypesBitmask();
}
@@ -9108,6 +9407,8 @@
*
* @return The bitmask of allowed network types.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -9133,10 +9434,14 @@
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
* @return the allowed network type bitmask
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
* @deprecated Use {@link #getAllowedNetworkTypesForReason} instead.
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@SystemApi
@Deprecated
public @NetworkTypeBitMask long getAllowedNetworkTypes() {
@@ -9161,6 +9466,9 @@
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -9247,6 +9555,9 @@
* tasks one at a time in serial order.
* @param callback Returns network scan results or errors.
* @return A NetworkScan obj which contains a callback which can be used to stop the scan.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(allOf = {
@@ -9295,11 +9606,15 @@
* tasks one at a time in serial order.
* @param callback Returns network scan results or errors.
* @return A NetworkScan obj which contains a callback which can be used to stop the scan.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(allOf = {
android.Manifest.permission.MODIFY_PHONE_STATE
})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public @Nullable NetworkScan requestNetworkScan(
@IncludeLocationData int includeLocationData,
@NonNull NetworkScanRequest request,
@@ -9317,6 +9632,9 @@
}
/**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+ *
* @deprecated
* Use {@link
* #requestNetworkScan(NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)}
@@ -9327,6 +9645,7 @@
android.Manifest.permission.MODIFY_PHONE_STATE,
Manifest.permission.ACCESS_FINE_LOCATION
})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public NetworkScan requestNetworkScan(
NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) {
return requestNetworkScan(request, AsyncTask.SERIAL_EXECUTOR, callback);
@@ -9347,6 +9666,9 @@
* attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
* normal network selection next time.
* @return {@code true} on success; {@code false} on any failure.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -9378,6 +9700,9 @@
* {@link AccessNetworkConstants.AccessNetworkType#UNKNOWN}, modem will select
* the next best RAN for network registration.
* @return {@code true} on success; {@code false} on any failure.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -9430,6 +9755,9 @@
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
* @return the network selection mode.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@SuppressAutoDoc // No support for carrier privileges (b/72967236).
@RequiresPermission(anyOf = {
@@ -9459,6 +9787,9 @@
* (see {@link #hasCarrierPrivileges})
*
* @return manually selected network info on success or empty string on failure
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@SuppressAutoDoc // No support carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -9488,6 +9819,8 @@
*
* @return {@code true} if this device is in emergency SMS mode, {@code false} otherwise.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
* @hide
*/
@SystemApi
@@ -9557,11 +9890,15 @@
*
* @param networkTypeBitmask The bitmask of preferred network types.
* @return true on success; false on any failure.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
* @deprecated Use {@link #setAllowedNetworkTypesForReason} instead.
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@SystemApi
public boolean setPreferredNetworkTypeBitmask(@NetworkTypeBitMask long networkTypeBitmask) {
try {
@@ -9603,6 +9940,10 @@
*
* @param allowedNetworkTypes The bitmask of allowed network types.
* @return true on success; false on any failure.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+ *
* @hide
* @deprecated Use {@link #setAllowedNetworkTypesForReason} instead with reason
* {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER}.
@@ -9690,11 +10031,16 @@
* @throws IllegalStateException if the Telephony process is not currently available.
* @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
* @throws SecurityException if the caller does not have the required privileges or if the
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* caller tries to use one of the following security-based reasons without
* {@link android.Manifest.permission#MODIFY_PHONE_STATE} permissions.
* <ol>
* <li>{@code TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}</li>
* </ol>
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(
@@ -9734,6 +10080,8 @@
* @throws IllegalStateException if the Telephony process is not currently available.
* @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
* @throws SecurityException if the caller does not have the required permission/privileges
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(
@@ -9808,6 +10156,9 @@
* <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
* @return true on success; false on any failure.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean setPreferredNetworkTypeToGlobal() {
@@ -9833,6 +10184,9 @@
* Requires Permission: MODIFY_PHONE_STATE.
*
* @return {@code true} if DUN APN is required for tethering.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
* @hide
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -9886,6 +10240,9 @@
* is a superset of the checks done in SubscriptionManager#canManageSubscription.
*
* @return true if the app has carrier privileges.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public boolean hasCarrierPrivileges() {
@@ -9933,6 +10290,9 @@
*
* @param brand The brand name to display/set.
* @return true if the operation was executed correctly.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public boolean setOperatorBrandOverride(String brand) {
@@ -10034,7 +10394,11 @@
* Expose the rest of ITelephony to @SystemApi
*/
- /** @hide */
+ /**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @hide
+ */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -10042,7 +10406,11 @@
return getCdmaMdn(getSubId());
}
- /** @hide */
+ /**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @hide
+ */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -10059,7 +10427,11 @@
}
}
- /** @hide */
+ /**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @hide
+ */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -10067,7 +10439,11 @@
return getCdmaMin(getSubId());
}
- /** @hide */
+ /**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+ * @hide
+ */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -10084,7 +10460,11 @@
}
}
- /** @hide */
+ /**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+ * @hide
+ */
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -10101,7 +10481,11 @@
return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
}
- /** @hide */
+ /**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+ * @hide
+ */
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -10118,14 +10502,22 @@
return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
}
- /** @hide */
+ /**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+ * @hide
+ */
@SystemApi
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public List<String> getCarrierPackageNamesForIntent(Intent intent) {
return getCarrierPackageNamesForIntentAndPhone(intent, getPhoneId());
}
- /** @hide */
+ /**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+ * @hide
+ */
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -10152,10 +10544,13 @@
* @return The system-selected package that provides the {@link CarrierService} implementation
* for the current subscription, or {@code null} if none is resolved
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @Nullable String getCarrierServicePackageName() {
return getCarrierServicePackageNameForLogicalSlot(getPhoneId());
}
@@ -10169,10 +10564,13 @@
* @return The system-selected package that provides the {@link CarrierService} implementation
* for the slot, or {@code null} if none is resolved
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @Nullable String getCarrierServicePackageNameForLogicalSlot(int logicalSlotIndex) {
try {
ITelephony telephony = getITelephony();
@@ -10206,6 +10604,8 @@
/**
* Get the names of packages with carrier privileges for all the active subscriptions.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@@ -10256,6 +10656,8 @@
*
* @throws IllegalArgumentException if requested state is invalid.
* @throws SecurityException if the caller does not have the permission.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
@@ -10285,6 +10687,9 @@
*
* @return the user-set status for enriched calling with call composer, either of
* {@link #CALL_COMPOSER_STATUS_ON} or {@link #CALL_COMPOSER_STATUS_OFF}.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
@@ -10301,7 +10706,11 @@
return CALL_COMPOSER_STATUS_OFF;
}
- /** @hide */
+ /**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+ * @hide
+ */
@SystemApi
@SuppressLint("RequiresPermission")
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
@@ -10316,6 +10725,9 @@
}
/**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+ *
* @deprecated Use {@link android.telecom.TelecomManager#placeCall(Uri address,
* Bundle extras)} instead.
* @hide
@@ -10323,6 +10735,7 @@
@Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.CALL_PHONE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void call(String callingPackage, String number) {
try {
ITelephony telephony = getITelephony();
@@ -10369,6 +10782,8 @@
}
/**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELECOM}.
* @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
* @hide
*/
@@ -10378,12 +10793,15 @@
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
android.Manifest.permission.READ_PHONE_STATE
})
+ @RequiresFeature(PackageManager.FEATURE_TELECOM)
public boolean isOffhook() {
TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE);
return tm.isInCall();
}
/**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELECOM}.
* @deprecated Use {@link android.telecom.TelecomManager#isRinging} instead
* @hide
*/
@@ -10393,12 +10811,15 @@
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
android.Manifest.permission.READ_PHONE_STATE
})
+ @RequiresFeature(PackageManager.FEATURE_TELECOM)
public boolean isRinging() {
TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE);
return tm.isRinging();
}
/**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELECOM}.
* @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
* @hide
*/
@@ -10408,12 +10829,15 @@
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
android.Manifest.permission.READ_PHONE_STATE
})
+ @RequiresFeature(PackageManager.FEATURE_TELECOM)
public boolean isIdle() {
TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE);
return !tm.isInCall();
}
/**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @deprecated Use {@link android.telephony.TelephonyManager#getServiceState} instead
* @hide
*/
@@ -10423,6 +10847,7 @@
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
android.Manifest.permission.READ_PHONE_STATE
})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean isRadioOn() {
try {
ITelephony telephony = getITelephony();
@@ -10434,7 +10859,11 @@
return false;
}
- /** @hide */
+ /**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+ * @hide
+ */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -10449,7 +10878,11 @@
return false;
}
- /** @hide */
+ /**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+ * @hide
+ */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -10465,11 +10898,15 @@
}
/**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+ *
* @deprecated use {@link #supplyIccLockPin(String)} instead.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@Deprecated
public int[] supplyPinReportResult(String pin) {
try {
@@ -10483,11 +10920,15 @@
}
/**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+ *
* @deprecated use {@link #supplyIccLockPuk(String, String)} instead.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@Deprecated
public int[] supplyPukReportResult(String puk, String pin) {
try {
@@ -10513,6 +10954,8 @@
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@@ -10549,6 +10992,8 @@
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@@ -10622,6 +11067,9 @@
* @param callback called by the framework to inform the caller of the result of executing the
* USSD request (see {@link UssdResponseCallback}).
* @param handler the {@link Handler} to run the request on.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@RequiresPermission(android.Manifest.permission.CALL_PHONE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -10666,6 +11114,9 @@
* voice and data simultaneously. This can change based on location or network condition.
*
* @return {@code true} if simultaneous voice and data supported, and {@code false} otherwise.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public boolean isConcurrentVoiceAndDataSupported() {
@@ -10679,9 +11130,13 @@
return false;
}
- /** @hide */
+ /**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+ * @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public boolean handlePinMmi(String dialString) {
try {
ITelephony telephony = getITelephony();
@@ -10693,9 +11148,14 @@
return false;
}
- /** @hide */
+ /**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+ * @hide
+ */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public boolean handlePinMmiForSubscriber(int subId, String dialString) {
try {
ITelephony telephony = getITelephony();
@@ -10707,7 +11167,11 @@
return false;
}
- /** @hide */
+ /**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+ * @hide
+ */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -10725,6 +11189,8 @@
* @deprecated - use the APIs {@link requestRadioPowerOffForReason} and
* {@link clearRadioPowerOffForReason}.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@Deprecated
@@ -10752,6 +11218,8 @@
* @deprecated - use the APIs {@link requestRadioPowerOffForReason} and
* {@link clearRadioPowerOffForReason}.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@Deprecated
@@ -10799,6 +11267,8 @@
* @throws SecurityException if the caller does not have MODIFY_PHONE_STATE permission.
* @throws IllegalStateException if the Telephony service is not currently available.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@SystemApi
@@ -10828,6 +11298,8 @@
* @throws SecurityException if the caller does not have MODIFY_PHONE_STATE permission.
* @throws IllegalStateException if the Telephony service is not currently available.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@SystemApi
@@ -10857,6 +11329,8 @@
* @throws SecurityException if the caller does not have READ_PRIVILEGED_PHONE_STATE permission.
* @throws IllegalStateException if the Telephony service is not currently available.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@SystemApi
@@ -10886,6 +11360,8 @@
* <p>To know when the radio has completed powering off, use
* {@link PhoneStateListener#LISTEN_SERVICE_STATE LISTEN_SERVICE_STATE}.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@SystemApi
@@ -10907,6 +11383,9 @@
* Check if any radio is on over all the slot indexes.
*
* @return {@code true} if any radio is on over any slot index.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@SystemApi
@@ -10953,6 +11432,8 @@
* {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@SystemApi
@@ -10982,7 +11463,11 @@
Log.e(TAG, "Do not call TelephonyManager#updateServiceLocation()");
}
- /** @hide */
+ /**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+ * @hide
+ */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
@@ -10997,7 +11482,11 @@
return false;
}
- /** @hide */
+ /**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+ * @hide
+ */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
@@ -11012,7 +11501,11 @@
return false;
}
- /** @hide */
+ /**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+ * @hide
+ */
@SystemApi
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public boolean isDataConnectivityPossible() {
@@ -11027,7 +11520,11 @@
return false;
}
- /** @hide */
+ /**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+ * @hide
+ */
@SystemApi
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean needsOtaServiceProvisioning() {
@@ -11078,23 +11575,29 @@
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
* @param enable Whether to enable mobile data.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
* @deprecated use setDataEnabledForReason with reason DATA_ENABLED_REASON_USER instead.
*
*/
@Deprecated
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public void setDataEnabled(boolean enable) {
setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
}
/**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
* @hide
* @deprecated use {@link #setDataEnabledForReason(int, boolean)} instead.
*/
@SystemApi
@Deprecated
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public void setDataEnabled(int subId, boolean enable) {
try {
setDataEnabledForReason(subId, DATA_ENABLED_REASON_USER, enable);
@@ -11105,10 +11608,14 @@
/**
* @deprecated use {@link #isDataEnabled()} instead.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
* @hide
*/
@SystemApi
@Deprecated
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public boolean getDataEnabled() {
return isDataEnabled();
}
@@ -11132,6 +11639,9 @@
* {@link ConnectivityManager#getRestrictBackgroundStatus}.
*
* @return true if mobile data is enabled.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
*/
@RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
android.Manifest.permission.MODIFY_PHONE_STATE,
@@ -11162,6 +11672,9 @@
* has carrier privileges (see {@link #hasCarrierPrivileges}).
*
* @return {@code true} if the data roaming is enabled on the subscription, otherwise return
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
* {@code false}.
*/
@RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
@@ -11201,6 +11714,8 @@
* {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
* @hide
*/
@SystemApi
@@ -11243,6 +11758,8 @@
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
* @hide
*/
@SystemApi
@@ -11311,6 +11828,8 @@
* {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
* @hide
*/
@SystemApi
@@ -11349,6 +11868,8 @@
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
* @hide
*/
@SystemApi
@@ -11384,6 +11905,8 @@
*
* @param isEnabled {@code true} to enable mobile data roaming, otherwise disable it.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
* @hide
*/
@SystemApi
@@ -11402,11 +11925,15 @@
}
/**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+ *
* @deprecated use {@link #isDataEnabled()} instead.
* @hide
*/
@Deprecated
@SystemApi
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public boolean getDataEnabled(int subId) {
try {
return isDataEnabledForReason(subId, DATA_ENABLED_REASON_USER);
@@ -11417,6 +11944,8 @@
}
/**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_IMS}.
* @deprecated Use {@link android.telephony.ims.ImsMmTelManager#setVtSettingEnabled(boolean)}
* instead.
* @hide
@@ -11424,6 +11953,7 @@
@Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
public void enableVideoCalling(boolean enable) {
try {
ITelephony telephony = getITelephony();
@@ -11435,6 +11965,8 @@
}
/**
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_IMS}.
* @deprecated Use {@link ImsMmTelManager#isVtSettingEnabled()} instead to check if the user
* has enabled the Video Calling setting, {@link ImsMmTelManager#isAvailable(int, int)} to
* determine if video calling is available, or {@link ImsMmTelManager#isCapable(int, int)} to
@@ -11447,6 +11979,7 @@
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
android.Manifest.permission.READ_PHONE_STATE
})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
public boolean isVideoCallingEnabled() {
try {
ITelephony telephony = getITelephony();
@@ -11462,6 +11995,9 @@
* Whether the device supports configuring the DTMF tone length.
*
* @return {@code true} if the DTMF tone length can be changed, and {@code false} otherwise.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public boolean canChangeDtmfToneLength() {
@@ -11483,7 +12019,11 @@
* Whether the device is a world phone.
*
* @return {@code true} if the device is a world phone, and {@code false} otherwise.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY}.
*/
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
public boolean isWorldPhone() {
try {
ITelephony telephony = getITelephony();
@@ -11504,8 +12044,11 @@
*
* @return {@code true} if the device supports TTY mode, and {@code false} otherwise.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELECOM}.
*/
@Deprecated
+ @RequiresFeature(PackageManager.FEATURE_TELECOM)
public boolean isTtyModeSupported() {
try {
TelecomManager telecomManager = null;
@@ -11526,6 +12069,9 @@
* support for the feature and device firmware support.
*
* @return {@code true} if the device and carrier both support RTT, {@code false} otherwise.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_IMS}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
public boolean isRttSupported() {
@@ -11546,6 +12092,9 @@
*
* @return {@code true} if the device supports hearing aid compatibility, and {@code false}
* otherwise.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public boolean isHearingAidCompatibilitySupported() {
@@ -11808,11 +12357,14 @@
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* {@hide}
**/
@SystemApi
@Deprecated
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public void setSimPowerState(int state) {
setSimPowerStateForSlot(getSlotIndex(), state);
}
@@ -11834,11 +12386,14 @@
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* {@hide}
**/
@SystemApi
@Deprecated
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public void setSimPowerStateForSlot(int slotIndex, int state) {
try {
ITelephony telephony = getITelephony();
@@ -11871,6 +12426,8 @@
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* {@hide}
**/
@SystemApi
@@ -11901,6 +12458,8 @@
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* {@hide}
**/
@SystemApi
@@ -12055,6 +12614,9 @@
* application currently configured for this user.
* @return component name of the app and class to direct Respond Via Message intent to, or
* {@code null} if the functionality is not supported.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
* @hide
*/
@SystemApi
@@ -12077,6 +12639,9 @@
* user associated with this subscription.
* @return component name of the app and class to direct Respond Via Message intent to, or
* {@code null} if the functionality is not supported.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
* @hide
*/
@SystemApi
@@ -12213,9 +12778,13 @@
* @return The {@link PhoneAccountHandle} associated with the TelphonyManager, or {@code null}
* if there is no associated {@link PhoneAccountHandle}; this can happen if the subscription is
* data-only or an opportunistic subscription.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public @Nullable PhoneAccountHandle getPhoneAccountHandle() {
return getPhoneAccountHandleForSubscriptionId(getSubId());
}
@@ -12277,10 +12846,14 @@
* Resets Telephony and IMS settings back to factory defaults only for the subscription
* associated with this instance.
* @see #createForSubscriptionId(int)
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY}.
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.CONNECTIVITY_INTERNAL)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
public void resetSettings() {
try {
Log.d(TAG, "resetSettings: subId=" + getSubId());
@@ -12302,6 +12875,8 @@
*
* @see Locale#toLanguageTag()
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@@ -12394,10 +12969,14 @@
* @param callback A callback object to which the result will be delivered. If there was an
* error processing the request, {@link OutcomeReceiver#onError} will be called
* with more details about the error.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY}.
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
public void requestModemActivityInfo(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<ModemActivityInfo, ModemActivityInfoException> callback) {
Objects.requireNonNull(executor);
@@ -12484,6 +13063,9 @@
* and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
* May return {@code null} when the subscription is inactive or when there was an error
* communicating with the phone process.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(allOf = {
@@ -12516,12 +13098,16 @@
* location related information.
* May return {@code null} when the subscription is inactive or when there was an error
* communicating with the phone process.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(allOf = {
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.ACCESS_COARSE_LOCATION
})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public @Nullable ServiceState getServiceState(@IncludeLocationData int includeLocationData) {
return getServiceStateForSubscriber(getSubId(),
includeLocationData != INCLUDE_LOCATION_DATA_FINE,
@@ -12580,6 +13166,9 @@
* voicemail ringtone.
* @return The URI for the ringtone to play when receiving a voicemail from a specific
* PhoneAccount. May be {@code null} if no ringtone is set.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public @Nullable Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) {
@@ -12606,10 +13195,13 @@
* @param uri The URI for the ringtone to play when receiving a voicemail from a specific
* PhoneAccount.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
* @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
* instead.
*/
@Deprecated
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void setVoicemailRingtoneUri(PhoneAccountHandle phoneAccountHandle, Uri uri) {
try {
ITelephony service = getITelephony();
@@ -12627,6 +13219,9 @@
* @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the
* voicemail vibration setting.
* @return {@code true} if the vibration is set for this PhoneAccount, {@code false} otherwise.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public boolean isVoicemailVibrationEnabled(PhoneAccountHandle accountHandle) {
@@ -12653,10 +13248,13 @@
* @param enabled Whether to enable or disable vibration for voicemail notifications from a
* specific PhoneAccount.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
* @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
* instead.
*/
@Deprecated
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public void setVoicemailVibrationEnabled(PhoneAccountHandle phoneAccountHandle,
boolean enabled) {
try {
@@ -12682,6 +13280,9 @@
*
* @return Carrier id of the current subscription. Return {@link #UNKNOWN_CARRIER_ID} if the
* subscription is unavailable or the carrier cannot be identified.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public int getSimCarrierId() {
@@ -12707,6 +13308,9 @@
*
* @return Carrier name of the current subscription. Return {@code null} if the subscription is
* unavailable or the carrier cannot be identified.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @Nullable CharSequence getSimCarrierIdName() {
@@ -12745,6 +13349,9 @@
* @return Returns fine-grained carrier id of the current subscription.
* Return {@link #UNKNOWN_CARRIER_ID} if the subscription is unavailable or the carrier cannot
* be identified.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public int getSimSpecificCarrierId() {
@@ -12771,6 +13378,9 @@
*
* @return user-facing name of the subscription specific carrier id. Return {@code null} if the
* subscription is unavailable or the carrier cannot be identified.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @Nullable CharSequence getSimSpecificCarrierIdName() {
@@ -12799,6 +13409,9 @@
*
* @return matching carrier id from sim MCCMNC. Return {@link #UNKNOWN_CARRIER_ID} if the
* subscription is unavailable or the carrier cannot be identified.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public int getCarrierIdFromSimMccMnc() {
@@ -12874,6 +13487,9 @@
*
* @param appType the uicc app type.
* @return Application ID for specified app type or {@code null} if no uicc or error.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@Nullable
@@ -12939,6 +13555,9 @@
* Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
*
* @return PRLVersion or null if error.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
* @hide
*/
@SystemApi
@@ -13004,6 +13623,9 @@
*
* @return The number of carriers set successfully. Should be length of
* carrierList on success; -1 if carrierList null or on error.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
* @hide
*/
@SystemApi
@@ -13130,6 +13752,9 @@
* @return {@link #SET_CARRIER_RESTRICTION_SUCCESS} in case of success.
* {@link #SET_CARRIER_RESTRICTION_NOT_SUPPORTED} if the modem does not support the
* configuration. {@link #SET_CARRIER_RESTRICTION_ERROR} in all other error cases.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
* @hide
*/
@SystemApi
@@ -13163,11 +13788,15 @@
*
* @return List of {@link android.telephony.CarrierIdentifier}; empty list
* means all carriers are allowed.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
* @hide
*/
@Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)
public List<CarrierIdentifier> getAllowedCarriers(int slotIndex) {
if (SubscriptionManager.isValidPhoneId(slotIndex)) {
CarrierRestrictionRules carrierRestrictionRule = getCarrierRestrictionRules();
@@ -13189,6 +13818,9 @@
* @return {@link CarrierRestrictionRules} which contains the allowed carrier list and the
* excluded carrier list with the priority between the two lists. Returns {@code null}
* in case of error.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
* @hide
*/
@SystemApi
@@ -13257,6 +13889,8 @@
* status result fetched from the radio
* @throws SecurityException if the caller does not have the required permission/privileges or
* if the caller is not pre-registered.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -13323,11 +13957,15 @@
* @see #resetAllCarrierActions()
* @deprecated use {@link #setDataEnabledForReason(int, boolean) with
* reason {@link #DATA_ENABLED_REASON_CARRIER}} instead.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
* @hide
*/
@Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public void setCarrierDataEnabled(boolean enabled) {
try {
setDataEnabledForReason(DATA_ENABLED_REASON_CARRIER, enabled);
@@ -13351,6 +13989,8 @@
* @deprecated - use the APIs {@link requestRadioPowerOffForReason} and
* {@link clearRadioPowerOffForReason}.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@Deprecated
@@ -13470,6 +14110,9 @@
*
* @param report control start/stop reporting network status.
* @see #resetAllCarrierActions()
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@SystemApi
@@ -13496,6 +14139,8 @@
*
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@@ -13618,6 +14263,8 @@
* has {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} irrespective of
* the reason.
* @throws IllegalStateException if the Telephony process is not currently available.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
@@ -13661,6 +14308,8 @@
* {@link android.Manifest.permission#MODIFY_PHONE_STATE}
* {@link android.Manifest.permission#READ_BASIC_PHONE_STATE}
* @throws IllegalStateException if the Telephony process is not currently available.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
*/
@RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
android.Manifest.permission.READ_PHONE_STATE,
@@ -13717,6 +14366,9 @@
* given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
*
* @return true if phone is in emergency callback mode.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
* @hide
*/
@SystemApi
@@ -13756,6 +14408,9 @@
* given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}.
*
* @return {@code true} if manual network selection is allowed, otherwise return {@code false}.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@SuppressAutoDoc // No support carrier privileges (b/72967236).
@RequiresPermission(anyOf = {android.Manifest.permission.READ_PRECISE_PHONE_STATE,
@@ -13779,6 +14434,9 @@
* Get the most recent SignalStrength information reported by the modem. Due
* to power saving this information may not always be current.
* @return the most recent cached signal strength info from the modem
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@Nullable
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -13805,6 +14463,9 @@
* <LI>And possibly others.</LI>
* </UL>
* @return {@code true} if the overall data connection is allowed; {@code false} if not.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
*/
@RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
android.Manifest.permission.READ_PHONE_STATE,
@@ -13975,6 +14636,9 @@
*
* @param enable enable(True) or disable(False)
* @return returns true if successfully set.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -14004,6 +14668,9 @@
* <p>
* Requires Permission:
* {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -14205,6 +14872,8 @@
* that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
* @throws SecurityException if the caller does not have the required permission
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -14240,6 +14909,8 @@
* <p> Requires permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
* @hide
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -14267,6 +14938,8 @@
* <p> Requires permission:
* {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
* @hide
*/
@RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
@@ -14293,6 +14966,8 @@
* <p> Requires permission:
* {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
* @hide
*/
@RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
@@ -14353,6 +15028,9 @@
* subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value
* as the list of {@link EmergencyNumber}; empty Map if this information is not available;
* or throw a SecurityException if the caller does not have the permission.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@NonNull
@@ -14409,6 +15087,8 @@
* as the list of {@link EmergencyNumber}; empty Map if this information is not available;
* or throw a SecurityException if the caller does not have the permission.
* @throws IllegalStateException if the Telephony process is not currently available.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@NonNull
@@ -14477,6 +15157,8 @@
* @return {@code true} if the given number is an emergency number based on current locale,
* SIM card(s), Android database, modem, network or defaults; {@code false} otherwise.
* @throws IllegalStateException if the Telephony process is not currently available.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
public boolean isEmergencyNumber(@NonNull String number) {
@@ -14514,6 +15196,8 @@
* network; {@code false} if it is not; or throw an SecurityException if the caller does not
* have the required permission/privileges
* @throws IllegalStateException if the Telephony process is not currently available.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*
* @deprecated Please use {@link TelephonyManager#isEmergencyNumber(String)} instead.
* @hide
@@ -14543,6 +15227,8 @@
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
* @hide
*/
@SystemApi
@@ -14694,6 +15380,9 @@
* @param callback Callback will be triggered once it succeeds or failed.
* See the {@code SET_OPPORTUNISTIC_SUB_*} constants
* for more details. Pass null if don't care about the result.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public void setPreferredOpportunisticDataSubscription(int subId, boolean needValidation,
@@ -14754,6 +15443,8 @@
* {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} if there are no preferred
* subscription id
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
*/
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
@@ -14793,6 +15484,8 @@
* @param executor The executor of where the callback will execute.
* @param callback Callback will be triggered once it succeeds or failed.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -14853,10 +15546,13 @@
* @param enable whether to enable or disable the modem stack.
* @return whether the operation is successful.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY}.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
public boolean enableModemForSlot(int slotIndex, boolean enable) {
boolean ret = false;
try {
@@ -14879,10 +15575,14 @@
* {@link #hasCarrierPrivileges()}).
*
* @param slotIndex which slot it's checking.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(anyOf = {android.Manifest.permission.READ_PHONE_STATE,
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
public boolean isModemEnabledForSlot(int slotIndex) {
try {
ITelephony telephony = getITelephony();
@@ -14945,6 +15645,8 @@
* @param isMultiSimCarrierRestricted true if usage of multiple SIMs is restricted, false
* otherwise.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
* @hide
*/
@SystemApi
@@ -15000,6 +15702,9 @@
* {@link #MULTISIM_NOT_SUPPORTED_BY_HARDWARE} if the device does not support multiple SIMs.
* {@link #MULTISIM_NOT_SUPPORTED_BY_CARRIER} in the device supports multiple SIMs, but the
* functionality is restricted by the carrier.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -15031,6 +15736,8 @@
*
* @param numOfSims number of live SIMs we want to switch to
* @throws android.os.RemoteException
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -15058,6 +15765,9 @@
*
* @return {@code true} if reboot will be triggered after making changes to modem
* configurations, otherwise return {@code false}.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*/
@RequiresPermission(Manifest.permission.READ_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -15220,6 +15930,8 @@
* {@link #CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED}, or
* {@link #CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES}
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@@ -15334,6 +16046,8 @@
* @param apnType Value indicating the apn type. Apn types are defined in {@link ApnSetting}.
* @return whether data is enabled for a apn type.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
* @hide
*/
@SystemApi
@@ -15356,6 +16070,8 @@
* Whether an APN type is metered or not. It will be evaluated with the subId associated
* with the TelephonyManager instance.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
* @hide
*/
@SystemApi
@@ -15385,6 +16101,9 @@
* @param executor The executor to execute the callback on
* @param callback The callback that gets invoked when the radio responds to the request. Called
* with {@code true} if the request succeeded, {@code false} otherwise.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@SystemApi
@@ -15403,6 +16122,9 @@
* Same as {@link #setSystemSelectionChannels(List, Executor, Consumer<Boolean>)}, but to be
* used when the caller does not need feedback on the results of the operation.
* @param specifiers which bands to scan.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@SystemApi
@@ -15450,6 +16172,8 @@
* @return a list of {@link RadioAccessSpecifier}, or an empty list if no bands are specified.
* @throws IllegalStateException if the Telephony process is not currently available.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@SystemApi
@@ -15478,6 +16202,8 @@
* @return {@code true} if input mccmnc and mvno matches with data from sim operator.
* {@code false} otherwise.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* {@hide}
*/
@SystemApi
@@ -15568,6 +16294,8 @@
* {@link CallForwardingInfo#REASON_UNCONDITIONAL}, {@link CallForwardingInfo#REASON_BUSY},
* {@link CallForwardingInfo#REASON_NO_REPLY}, {@link CallForwardingInfo#REASON_NOT_REACHABLE},
* {@link CallForwardingInfo#REASON_ALL}, or {@link CallForwardingInfo#REASON_ALL_CONDITIONAL}
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
*
* @hide
*/
@@ -15645,6 +16373,8 @@
* <li>{@link CallForwardingInfo#getTimeoutSeconds()} returns a non-positive value when
* enabling call forwarding</li>
* </ul>
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
* @hide
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -15769,6 +16499,9 @@
* <li>{@link #CALL_WAITING_STATUS_NOT_SUPPORTED}}</li>
* <li>{@link #CALL_WAITING_STATUS_FDN_CHECK_FAILURE}}</li>
* </ul>
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
* @hide
*/
@SystemApi
@@ -15819,6 +16552,9 @@
* {@link #CALL_WAITING_STATUS_NOT_SUPPORTED} or
* {@link #CALL_WAITING_STATUS_UNKNOWN_ERROR} or
* {@link #CALL_WAITING_STATUS_FDN_CHECK_FAILURE} if it failed.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
* @hide
*/
@SystemApi
@@ -15919,6 +16655,9 @@
*
* @param policy The data policy to enable.
* @param enabled Whether to enable or disable the policy.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
* @hide
*/
@SystemApi
@@ -15940,6 +16679,9 @@
*
* @param policy The data policy that you want the status for.
* @return {@code true} if enabled, {@code false} otherwise.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
* @hide
*/
@SystemApi
@@ -15975,6 +16717,8 @@
* {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@WorkerThread
@@ -16009,6 +16753,8 @@
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@@ -16051,6 +16797,8 @@
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@@ -16199,6 +16947,8 @@
* </ol>
* @return operation result.
* @throws IllegalStateException if the Telephony process is not currently available.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@SystemApi
@@ -16233,6 +16983,8 @@
* connectivity is active. It means the device is allowed to connect to both primary and
* secondary cell.
* @throws IllegalStateException if the Telephony process is not currently available.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@SystemApi
@@ -16470,6 +17222,8 @@
*
* @throws IllegalStateException if the Telephony process is not currently available.
* @throws SecurityException if the caller doesn't have the permission.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
*
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -16586,6 +17340,9 @@
*
* @param capability the name of the capability to check for
* @return the availability of the capability
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public boolean isRadioInterfaceCapabilitySupported(
@@ -16705,6 +17462,8 @@
* @throws IllegalArgumentException if the thermalMitigationRequest had invalid parameters or
* if the device's modem does not support data throttling.
*
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
* @hide
*/
@SystemApi
@@ -17037,6 +17796,9 @@
* contain the GBA Ks_NAF/Ks_ext_NAF when available. If the NAF keys are
* available and valid at the time of call and bootstrapping is not requested,
* then the callback shall be invoked with the available keys.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@@ -17135,6 +17897,8 @@
* @param request the SignalStrengthUpdateRequest to be set into the System
*
* @throws IllegalStateException if a new request is set with same subId from the same caller
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -17165,6 +17929,9 @@
* @see #setSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)
*
* @param request the SignalStrengthUpdateRequest to be cleared from the System
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -17188,10 +17955,14 @@
* @return the PhoneCapability which describes the data connection capability of modem.
* It's used to evaluate possible phone config change, for example from single
* SIM device to multi-SIM device.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public @NonNull PhoneCapability getPhoneCapability() {
try {
ITelephony telephony = getITelephony();
@@ -17256,11 +18027,15 @@
* at least one SIM card for which the user needs to manually enter the PIN
* code after the reboot. {@link #PREPARE_UNATTENDED_REBOOT_ERROR} in case
* of error.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.REBOOT)
@PrepareUnattendedRebootResult
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
public int prepareForUnattendedReboot() {
try {
ITelephony service = getITelephony();
@@ -17363,6 +18138,9 @@
*
* @param executor the executor on which callback will be invoked.
* @param callback a callback to receive the current slicing configuration.
+ *
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
*/
@RequiresFeature(
enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
@@ -17444,8 +18222,11 @@
* @param capability The premium capability to check.
* @return Whether the given premium capability is available to purchase.
* @throws SecurityException if the caller does not hold permission READ_BASIC_PHONE_STATE.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
*/
@RequiresPermission(android.Manifest.permission.READ_BASIC_PHONE_STATE)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public boolean isPremiumCapabilityAvailableForPurchase(@PremiumCapability int capability) {
try {
ITelephony telephony = getITelephony();
@@ -17685,10 +18466,13 @@
* @param callback The result of the purchase request.
* @throws SecurityException if the caller does not hold permissions
* READ_BASIC_PHONE_STATE or INTERNET.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_DATA}.
* @see #isPremiumCapabilityAvailableForPurchase(int) to check whether the capability is valid.
*/
@RequiresPermission(allOf = {android.Manifest.permission.READ_BASIC_PHONE_STATE,
android.Manifest.permission.INTERNET})
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
public void purchasePremiumCapability(@PremiumCapability int capability,
@NonNull @CallbackExecutor Executor executor,
@NonNull @PurchasePremiumCapabilityResult Consumer<Integer> callback) {
@@ -18142,10 +18926,14 @@
* Get current cell broadcast message identifier ranges.
*
* @throws SecurityException if the caller does not have the required permission
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+ *
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
@NonNull
public List<CellBroadcastIdRange> getCellBroadcastIdRanges() {
try {
@@ -18299,10 +19087,13 @@
* the result when the operation completes.
* @throws SecurityException if the caller does not have the required permission
* @throws IllegalArgumentException when the ranges are invalid.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
+ @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
public void setCellBroadcastIdRanges(@NonNull List<CellBroadcastIdRange> ranges,
@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<Integer> callback) {
@@ -18378,7 +19169,8 @@
* </ul>
*
* @return Primary IMEI of type string
- * @throws UnsupportedOperationException if the radio doesn't support this feature.
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_GSM}.
* @throws SecurityException if the caller does not have the required permission/privileges
*/
@NonNull
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index 9c33576..bbd4567 100644
--- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -36,12 +36,12 @@
import android.view.InputDevice
import android.view.inputmethod.InputMethodInfo
import android.view.inputmethod.InputMethodSubtype
-import androidx.test.core.R
import androidx.test.core.app.ApplicationProvider
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.internal.os.KeyboardConfiguredProto
import com.android.internal.util.FrameworkStatsLog
import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.test.input.R
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals